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 { None = 0x0, Libravatar = 0x1, Gravatar = 0x2 };
32 int mBackends = Gravatar;
33
34 bool mHasGravatar = false;
35 bool mUseDefaultPixmap = false;
36};
37
38GravatarResolvUrlJob::GravatarResolvUrlJob(QObject *parent)
39 : QObject(parent)
40 , d(new Gravatar::GravatarResolvUrlJobPrivate)
41{
42}
43
44GravatarResolvUrlJob::~GravatarResolvUrlJob() = default;
45
46bool GravatarResolvUrlJob::canStart() const
47{
48 if (PimCommon::NetworkManager::self()->isOnline()) {
49 // qCDebug(GRAVATAR_LOG) << "email " << d->mEmail;
50 return !d->mEmail.trimmed().isEmpty() && (d->mEmail.contains(QLatin1Char('@')));
51 } else {
52 return false;
53 }
54}
55
56QUrl GravatarResolvUrlJob::generateGravatarUrl(bool useLibravatar)
57{
58 return createUrl(useLibravatar);
59}
60
61bool GravatarResolvUrlJob::hasGravatar() const
62{
63 return d->mHasGravatar;
64}
65
66void GravatarResolvUrlJob::startNetworkManager(const QUrl &url)
67{
68 if (!d->mNetworkAccessManager) {
69 d->mNetworkAccessManager = new QNetworkAccessManager(this);
70 d->mNetworkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
71 d->mNetworkAccessManager->setStrictTransportSecurityEnabled(true);
72 d->mNetworkAccessManager->enableStrictTransportSecurityStore(true);
73 connect(d->mNetworkAccessManager, &QNetworkAccessManager::finished, this, &GravatarResolvUrlJob::slotFinishLoadPixmap);
74 }
75
76 QNetworkRequest req(url);
78 req.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
79 d->mNetworkAccessManager->get(req);
80}
81
82void GravatarResolvUrlJob::start()
83{
84 if (d->mBackends == GravatarResolvUrlJobPrivate::None) {
85 d->mBackends = GravatarResolvUrlJobPrivate::Gravatar; // default is Gravatar if nothing else is selected
86 }
87
88 d->mHasGravatar = false;
89 if (canStart()) {
90 processNextBackend();
91 } else {
92 qCDebug(GRAVATAR_LOG) << "Gravatar can not start";
94 }
95}
96
97void GravatarResolvUrlJob::processNextBackend()
98{
99 if (d->mHasGravatar || d->mBackends == GravatarResolvUrlJobPrivate::None) {
100 Q_EMIT finished(this);
101 deleteLater();
102 return;
103 }
104
105 QUrl url;
106 if (d->mBackends & GravatarResolvUrlJobPrivate::Libravatar) {
107 d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar;
108 url = createUrl(true);
109 } else if (d->mBackends & GravatarResolvUrlJobPrivate::Gravatar) {
110 d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar;
111 url = createUrl(false);
112 }
113
114 // qDebug() << " url " << url;
115 Q_EMIT resolvUrl(url);
116 if (!cacheLookup(d->mCalculatedHash)) {
117 startNetworkManager(url);
118 } else {
119 processNextBackend();
120 }
121}
122
123void GravatarResolvUrlJob::slotFinishLoadPixmap(QNetworkReply *reply)
124{
125 if (reply->error() == QNetworkReply::NoError) {
126 const QByteArray data = reply->readAll();
127 d->mPixmap.loadFromData(data);
128 d->mHasGravatar = true;
129 // For the moment don't use cache other we will store a lot of pixmap
130 if (!d->mUseDefaultPixmap) {
131 GravatarCache::self()->saveGravatarPixmap(d->mCalculatedHash, d->mPixmap);
132 }
133 } else {
135 GravatarCache::self()->saveMissingGravatar(d->mCalculatedHash);
136 } else {
137 qCDebug(GRAVATAR_LOG) << "Network error:" << reply->request().url() << reply->errorString();
138 }
139 }
140 reply->deleteLater();
141
142 processNextBackend();
143}
144
145QString GravatarResolvUrlJob::email() const
146{
147 return d->mEmail;
148}
149
150void GravatarResolvUrlJob::setEmail(const QString &email)
151{
152 d->mEmail = email;
153}
154
155Hash GravatarResolvUrlJob::calculateHash()
156{
157 const auto email = d->mEmail.toLower().toUtf8();
158 return Hash(QCryptographicHash::hash(email, QCryptographicHash::Md5), Hash::Md5);
159}
160
161bool GravatarResolvUrlJob::fallbackGravatar() const
162{
163 return d->mBackends & GravatarResolvUrlJobPrivate::Gravatar;
164}
165
166void GravatarResolvUrlJob::setFallbackGravatar(bool fallbackGravatar)
167{
168 if (fallbackGravatar) {
169 d->mBackends |= GravatarResolvUrlJobPrivate::Gravatar;
170 } else {
171 d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar;
172 }
173}
174
175bool GravatarResolvUrlJob::useLibravatar() const
176{
177 return d->mBackends & GravatarResolvUrlJobPrivate::Libravatar;
178}
179
180void GravatarResolvUrlJob::setUseLibravatar(bool useLibravatar)
181{
182 if (useLibravatar) {
183 d->mBackends |= GravatarResolvUrlJobPrivate::Libravatar;
184 } else {
185 d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar;
186 }
187}
188
189bool GravatarResolvUrlJob::useDefaultPixmap() const
190{
191 return d->mUseDefaultPixmap;
192}
193
194void GravatarResolvUrlJob::setUseDefaultPixmap(bool useDefaultPixmap)
195{
196 d->mUseDefaultPixmap = useDefaultPixmap;
197}
198
199int GravatarResolvUrlJob::size() const
200{
201 return d->mSize;
202}
203
204QPixmap GravatarResolvUrlJob::pixmap() const
205{
206 return d->mPixmap;
207}
208
209void GravatarResolvUrlJob::setSize(int size)
210{
211 if (size <= 0) {
212 size = 80;
213 } else if (size > 2048) {
214 size = 2048;
215 }
216 d->mSize = size;
217}
218
219Hash GravatarResolvUrlJob::calculatedHash() const
220{
221 return d->mCalculatedHash;
222}
223
224QUrl GravatarResolvUrlJob::createUrl(bool useLibravatar)
225{
226 QUrl url;
227 d->mCalculatedHash = Hash();
228 if (!canStart()) {
229 return url;
230 }
232 if (!d->mUseDefaultPixmap) {
233 // Add ?d=404
234 query.addQueryItem(QStringLiteral("d"), QStringLiteral("404"));
235 }
236 if (d->mSize != 80) {
237 query.addQueryItem(QStringLiteral("s"), QString::number(d->mSize));
238 }
239 url.setScheme(QStringLiteral("https"));
240 if (useLibravatar) {
241 url.setHost(QStringLiteral("seccdn.libravatar.org"));
242 } else {
243 url.setHost(QStringLiteral("secure.gravatar.com"));
244 }
245 d->mCalculatedHash = calculateHash();
246 url.setPath("/avatar/"_L1 + d->mCalculatedHash.hexString());
247 url.setQuery(query);
248 return url;
249}
250
251bool GravatarResolvUrlJob::cacheLookup(const Hash &hash)
252{
253 bool haveStoredPixmap = false;
254 const QPixmap pix = GravatarCache::self()->loadGravatarPixmap(hash, haveStoredPixmap);
255 if (haveStoredPixmap && !pix.isNull()) { // we know a Gravatar for this hash
256 d->mPixmap = pix;
257 d->mHasGravatar = true;
258 Q_EMIT finished(this);
259 deleteLater();
260 return true;
261 }
262 return haveStoredPixmap;
263}
264
265#include "moc_gravatarresolvurljob.cpp"
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
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 Fri Oct 4 2024 12:05:39 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.