File indexing completed on 2024-11-24 04:49:02
0001 /* 0002 SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "gravatarresolvurljob.h" 0008 #include "gravatar_debug.h" 0009 #include "misc/gravatarcache.h" 0010 #include "misc/hash.h" 0011 #include <PimCommon/NetworkManager> 0012 0013 #include <QCryptographicHash> 0014 #include <QNetworkReply> 0015 #include <QUrlQuery> 0016 0017 using namespace Gravatar; 0018 0019 class Gravatar::GravatarResolvUrlJobPrivate 0020 { 0021 public: 0022 GravatarResolvUrlJobPrivate() = default; 0023 QPixmap mPixmap; 0024 QString mEmail; 0025 Hash mCalculatedHash; 0026 QNetworkAccessManager *mNetworkAccessManager = nullptr; 0027 int mSize = 80; 0028 0029 enum Backend { None = 0x0, Libravatar = 0x1, Gravatar = 0x2 }; 0030 int mBackends = Gravatar; 0031 0032 bool mHasGravatar = false; 0033 bool mUseDefaultPixmap = false; 0034 }; 0035 0036 GravatarResolvUrlJob::GravatarResolvUrlJob(QObject *parent) 0037 : QObject(parent) 0038 , d(new Gravatar::GravatarResolvUrlJobPrivate) 0039 { 0040 } 0041 0042 GravatarResolvUrlJob::~GravatarResolvUrlJob() = default; 0043 0044 bool GravatarResolvUrlJob::canStart() const 0045 { 0046 if (PimCommon::NetworkManager::self()->isOnline()) { 0047 // qCDebug(GRAVATAR_LOG) << "email " << d->mEmail; 0048 return !d->mEmail.trimmed().isEmpty() && (d->mEmail.contains(QLatin1Char('@'))); 0049 } else { 0050 return false; 0051 } 0052 } 0053 0054 QUrl GravatarResolvUrlJob::generateGravatarUrl(bool useLibravatar) 0055 { 0056 return createUrl(useLibravatar); 0057 } 0058 0059 bool GravatarResolvUrlJob::hasGravatar() const 0060 { 0061 return d->mHasGravatar; 0062 } 0063 0064 void GravatarResolvUrlJob::startNetworkManager(const QUrl &url) 0065 { 0066 if (!d->mNetworkAccessManager) { 0067 d->mNetworkAccessManager = new QNetworkAccessManager(this); 0068 d->mNetworkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); 0069 d->mNetworkAccessManager->setStrictTransportSecurityEnabled(true); 0070 d->mNetworkAccessManager->enableStrictTransportSecurityStore(true); 0071 connect(d->mNetworkAccessManager, &QNetworkAccessManager::finished, this, &GravatarResolvUrlJob::slotFinishLoadPixmap); 0072 } 0073 0074 QNetworkRequest req(url); 0075 req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); 0076 req.setAttribute(QNetworkRequest::Http2AllowedAttribute, true); 0077 d->mNetworkAccessManager->get(req); 0078 } 0079 0080 void GravatarResolvUrlJob::start() 0081 { 0082 if (d->mBackends == GravatarResolvUrlJobPrivate::None) { 0083 d->mBackends = GravatarResolvUrlJobPrivate::Gravatar; // default is Gravatar if nothing else is selected 0084 } 0085 0086 d->mHasGravatar = false; 0087 if (canStart()) { 0088 processNextBackend(); 0089 } else { 0090 qCDebug(GRAVATAR_LOG) << "Gravatar can not start"; 0091 deleteLater(); 0092 } 0093 } 0094 0095 void GravatarResolvUrlJob::processNextBackend() 0096 { 0097 if (d->mHasGravatar || d->mBackends == GravatarResolvUrlJobPrivate::None) { 0098 Q_EMIT finished(this); 0099 deleteLater(); 0100 return; 0101 } 0102 0103 QUrl url; 0104 if (d->mBackends & GravatarResolvUrlJobPrivate::Libravatar) { 0105 d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar; 0106 url = createUrl(true); 0107 } else if (d->mBackends & GravatarResolvUrlJobPrivate::Gravatar) { 0108 d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar; 0109 url = createUrl(false); 0110 } 0111 0112 // qDebug() << " url " << url; 0113 Q_EMIT resolvUrl(url); 0114 if (!cacheLookup(d->mCalculatedHash)) { 0115 startNetworkManager(url); 0116 } else { 0117 processNextBackend(); 0118 } 0119 } 0120 0121 void GravatarResolvUrlJob::slotFinishLoadPixmap(QNetworkReply *reply) 0122 { 0123 if (reply->error() == QNetworkReply::NoError) { 0124 const QByteArray data = reply->readAll(); 0125 d->mPixmap.loadFromData(data); 0126 d->mHasGravatar = true; 0127 // For the moment don't use cache other we will store a lot of pixmap 0128 if (!d->mUseDefaultPixmap) { 0129 GravatarCache::self()->saveGravatarPixmap(d->mCalculatedHash, d->mPixmap); 0130 } 0131 } else { 0132 if (reply->error() != QNetworkReply::ContentNotFoundError) { 0133 GravatarCache::self()->saveMissingGravatar(d->mCalculatedHash); 0134 } else { 0135 qCDebug(GRAVATAR_LOG) << "Network error:" << reply->request().url() << reply->errorString(); 0136 } 0137 } 0138 reply->deleteLater(); 0139 0140 processNextBackend(); 0141 } 0142 0143 QString GravatarResolvUrlJob::email() const 0144 { 0145 return d->mEmail; 0146 } 0147 0148 void GravatarResolvUrlJob::setEmail(const QString &email) 0149 { 0150 d->mEmail = email; 0151 } 0152 0153 Hash GravatarResolvUrlJob::calculateHash() 0154 { 0155 const auto email = d->mEmail.toLower().toUtf8(); 0156 return Hash(QCryptographicHash::hash(email, QCryptographicHash::Md5), Hash::Md5); 0157 } 0158 0159 bool GravatarResolvUrlJob::fallbackGravatar() const 0160 { 0161 return d->mBackends & GravatarResolvUrlJobPrivate::Gravatar; 0162 } 0163 0164 void GravatarResolvUrlJob::setFallbackGravatar(bool fallbackGravatar) 0165 { 0166 if (fallbackGravatar) { 0167 d->mBackends |= GravatarResolvUrlJobPrivate::Gravatar; 0168 } else { 0169 d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar; 0170 } 0171 } 0172 0173 bool GravatarResolvUrlJob::useLibravatar() const 0174 { 0175 return d->mBackends & GravatarResolvUrlJobPrivate::Libravatar; 0176 } 0177 0178 void GravatarResolvUrlJob::setUseLibravatar(bool useLibravatar) 0179 { 0180 if (useLibravatar) { 0181 d->mBackends |= GravatarResolvUrlJobPrivate::Libravatar; 0182 } else { 0183 d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar; 0184 } 0185 } 0186 0187 bool GravatarResolvUrlJob::useDefaultPixmap() const 0188 { 0189 return d->mUseDefaultPixmap; 0190 } 0191 0192 void GravatarResolvUrlJob::setUseDefaultPixmap(bool useDefaultPixmap) 0193 { 0194 d->mUseDefaultPixmap = useDefaultPixmap; 0195 } 0196 0197 int GravatarResolvUrlJob::size() const 0198 { 0199 return d->mSize; 0200 } 0201 0202 QPixmap GravatarResolvUrlJob::pixmap() const 0203 { 0204 return d->mPixmap; 0205 } 0206 0207 void GravatarResolvUrlJob::setSize(int size) 0208 { 0209 if (size <= 0) { 0210 size = 80; 0211 } else if (size > 2048) { 0212 size = 2048; 0213 } 0214 d->mSize = size; 0215 } 0216 0217 Hash GravatarResolvUrlJob::calculatedHash() const 0218 { 0219 return d->mCalculatedHash; 0220 } 0221 0222 QUrl GravatarResolvUrlJob::createUrl(bool useLibravatar) 0223 { 0224 QUrl url; 0225 d->mCalculatedHash = Hash(); 0226 if (!canStart()) { 0227 return url; 0228 } 0229 QUrlQuery query; 0230 if (!d->mUseDefaultPixmap) { 0231 // Add ?d=404 0232 query.addQueryItem(QStringLiteral("d"), QStringLiteral("404")); 0233 } 0234 if (d->mSize != 80) { 0235 query.addQueryItem(QStringLiteral("s"), QString::number(d->mSize)); 0236 } 0237 url.setScheme(QStringLiteral("https")); 0238 if (useLibravatar) { 0239 url.setHost(QStringLiteral("seccdn.libravatar.org")); 0240 } else { 0241 url.setHost(QStringLiteral("secure.gravatar.com")); 0242 } 0243 d->mCalculatedHash = calculateHash(); 0244 url.setPath(QLatin1StringView("/avatar/") + d->mCalculatedHash.hexString()); 0245 url.setQuery(query); 0246 return url; 0247 } 0248 0249 bool GravatarResolvUrlJob::cacheLookup(const Hash &hash) 0250 { 0251 bool haveStoredPixmap = false; 0252 const QPixmap pix = GravatarCache::self()->loadGravatarPixmap(hash, haveStoredPixmap); 0253 if (haveStoredPixmap && !pix.isNull()) { // we know a Gravatar for this hash 0254 d->mPixmap = pix; 0255 d->mHasGravatar = true; 0256 Q_EMIT finished(this); 0257 deleteLater(); 0258 return true; 0259 } 0260 return haveStoredPixmap; 0261 } 0262 0263 #include "moc_gravatarresolvurljob.cpp"