gravatarresolvurljob.cpp 7.02 KB
Newer Older
1
/*
Laurent Montel's avatar
Laurent Montel committed
2
   SPDX-FileCopyrightText: 2015-2021 Laurent Montel <montel@kde.org>
Laurent Montel's avatar
Laurent Montel committed
3

4
   SPDX-License-Identifier: LGPL-2.0-or-later
5
6
7
*/

#include "gravatarresolvurljob.h"
Laurent Montel's avatar
Laurent Montel committed
8
#include "gravatar_debug.h"
9
#include "misc/gravatarcache.h"
10
#include "misc/hash.h"
11
#include <PimCommon/NetworkManager>
12
13
14

#include <QCryptographicHash>
#include <QNetworkConfigurationManager>
Laurent Montel's avatar
Laurent Montel committed
15
#include <QNetworkReply>
Laurent Montel's avatar
Laurent Montel committed
16
#include <QUrlQuery>
17
18
19

using namespace Gravatar;

Laurent Montel's avatar
Laurent Montel committed
20
class Q_DECL_HIDDEN Gravatar::GravatarResolvUrlJobPrivate
21
22
23
24
25
26
27
28
{
public:
    GravatarResolvUrlJobPrivate()
    {
    }

    QPixmap mPixmap;
    QString mEmail;
29
    Hash mCalculatedHash;
Laurent Montel's avatar
Laurent Montel committed
30
31
    QNetworkAccessManager *mNetworkAccessManager = nullptr;
    int mSize = 80;
32

Laurent Montel's avatar
Laurent Montel committed
33
    enum Backend { None = 0x0, Libravatar = 0x1, Gravatar = 0x2 };
Laurent Montel's avatar
Laurent Montel committed
34
    int mBackends = Gravatar;
35

Laurent Montel's avatar
Laurent Montel committed
36
37
    bool mHasGravatar = false;
    bool mUseDefaultPixmap = false;
38
39
40
};

GravatarResolvUrlJob::GravatarResolvUrlJob(QObject *parent)
Laurent Montel's avatar
Laurent Montel committed
41
42
    : QObject(parent)
    , d(new Gravatar::GravatarResolvUrlJobPrivate)
43
44
45
46
47
48
49
50
51
52
{
}

GravatarResolvUrlJob::~GravatarResolvUrlJob()
{
    delete d;
}

bool GravatarResolvUrlJob::canStart() const
{
53
    if (PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline()) {
Laurent Montel's avatar
Laurent Montel committed
54
        // qCDebug(GRAVATAR_LOG) << "email " << d->mEmail;
55
56
57
58
59
60
        return !d->mEmail.trimmed().isEmpty() && (d->mEmail.contains(QLatin1Char('@')));
    } else {
        return false;
    }
}

Laurent Montel's avatar
Laurent Montel committed
61
QUrl GravatarResolvUrlJob::generateGravatarUrl(bool useLibravatar)
62
{
Laurent Montel's avatar
Laurent Montel committed
63
    return createUrl(useLibravatar);
64
65
66
67
68
69
70
71
72
}

bool GravatarResolvUrlJob::hasGravatar() const
{
    return d->mHasGravatar;
}

void GravatarResolvUrlJob::startNetworkManager(const QUrl &url)
{
73
74
    if (!d->mNetworkAccessManager) {
        d->mNetworkAccessManager = new QNetworkAccessManager(this);
Laurent Montel's avatar
Laurent Montel committed
75
        d->mNetworkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
76
77
        d->mNetworkAccessManager->setStrictTransportSecurityEnabled(true);
        d->mNetworkAccessManager->enableStrictTransportSecurityStore(true);
78
        connect(d->mNetworkAccessManager, &QNetworkAccessManager::finished, this, &GravatarResolvUrlJob::slotFinishLoadPixmap);
79
    }
Laurent Montel's avatar
Laurent Montel committed
80
81
82
83
84

    QNetworkRequest req(url);
    req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
    req.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
    d->mNetworkAccessManager->get(req);
85
86
87
88
}

void GravatarResolvUrlJob::start()
{
Laurent Montel's avatar
Laurent Montel committed
89
    if (d->mBackends == GravatarResolvUrlJobPrivate::None) {
90
        d->mBackends = GravatarResolvUrlJobPrivate::Gravatar; // default is Gravatar if nothing else is selected
Laurent Montel's avatar
Laurent Montel committed
91
    }
92

93
94
    d->mHasGravatar = false;
    if (canStart()) {
95
        processNextBackend();
96
97
98
99
100
101
    } else {
        qCDebug(GRAVATAR_LOG) << "Gravatar can not start";
        deleteLater();
    }
}

102
103
104
105
106
107
108
109
110
void GravatarResolvUrlJob::processNextBackend()
{
    if (d->mHasGravatar || d->mBackends == GravatarResolvUrlJobPrivate::None) {
        Q_EMIT finished(this);
        deleteLater();
        return;
    }

    QUrl url;
Laurent Montel's avatar
Laurent Montel committed
111
112
113
114
    if (d->mBackends & GravatarResolvUrlJobPrivate::Libravatar) {
        d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar;
        url = createUrl(true);
    } else if (d->mBackends & GravatarResolvUrlJobPrivate::Gravatar) {
115
        d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar;
Laurent Montel's avatar
Laurent Montel committed
116
        url = createUrl(false);
117
118
    }

Laurent Montel's avatar
Laurent Montel committed
119
    // qDebug() << " url " << url;
120
    Q_EMIT resolvUrl(url);
Laurent Montel's avatar
Laurent Montel committed
121
    if (!cacheLookup(d->mCalculatedHash)) {
122
        startNetworkManager(url);
Laurent Montel's avatar
Laurent Montel committed
123
    } else {
124
        processNextBackend();
Laurent Montel's avatar
Laurent Montel committed
125
    }
126
127
}

128
129
void GravatarResolvUrlJob::slotFinishLoadPixmap(QNetworkReply *reply)
{
Laurent Montel's avatar
Laurent Montel committed
130
    if (reply->error() == QNetworkReply::NoError) {
Laurent Montel's avatar
Laurent Montel committed
131
132
        const QByteArray data = reply->readAll();
        d->mPixmap.loadFromData(data);
133
        d->mHasGravatar = true;
Laurent Montel's avatar
Laurent Montel committed
134
        // For the moment don't use cache other we will store a lot of pixmap
135
136
137
        if (!d->mUseDefaultPixmap) {
            GravatarCache::self()->saveGravatarPixmap(d->mCalculatedHash, d->mPixmap);
        }
138
    } else {
139
        if (reply->error() != QNetworkReply::ContentNotFoundError) {
140
            GravatarCache::self()->saveMissingGravatar(d->mCalculatedHash);
Laurent Montel's avatar
Laurent Montel committed
141
        } else {
142
            qCDebug(GRAVATAR_LOG) << "Network error:" << reply->request().url() << reply->errorString();
Laurent Montel's avatar
Laurent Montel committed
143
        }
144
    }
145
    reply->deleteLater();
146

147
    processNextBackend();
148
149
150
151
152
153
154
155
156
157
158
159
}

QString GravatarResolvUrlJob::email() const
{
    return d->mEmail;
}

void GravatarResolvUrlJob::setEmail(const QString &email)
{
    d->mEmail = email;
}

Laurent Montel's avatar
Laurent Montel committed
160
Hash GravatarResolvUrlJob::calculateHash()
161
{
162
163
    const auto email = d->mEmail.toLower().toUtf8();
    return Hash(QCryptographicHash::hash(email, QCryptographicHash::Md5), Hash::Md5);
164
165
}

Laurent Montel's avatar
Laurent Montel committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
bool GravatarResolvUrlJob::fallbackGravatar() const
{
    return d->mBackends & GravatarResolvUrlJobPrivate::Gravatar;
}

void GravatarResolvUrlJob::setFallbackGravatar(bool fallbackGravatar)
{
    if (fallbackGravatar)
        d->mBackends |= GravatarResolvUrlJobPrivate::Gravatar;
    else
        d->mBackends &= ~GravatarResolvUrlJobPrivate::Gravatar;
}

bool GravatarResolvUrlJob::useLibravatar() const
{
    return d->mBackends & GravatarResolvUrlJobPrivate::Libravatar;
}

void GravatarResolvUrlJob::setUseLibravatar(bool useLibravatar)
{
    if (useLibravatar)
        d->mBackends |= GravatarResolvUrlJobPrivate::Libravatar;
    else
        d->mBackends &= ~GravatarResolvUrlJobPrivate::Libravatar;
}

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
bool GravatarResolvUrlJob::useDefaultPixmap() const
{
    return d->mUseDefaultPixmap;
}

void GravatarResolvUrlJob::setUseDefaultPixmap(bool useDefaultPixmap)
{
    d->mUseDefaultPixmap = useDefaultPixmap;
}

int GravatarResolvUrlJob::size() const
{
    return d->mSize;
}

QPixmap GravatarResolvUrlJob::pixmap() const
{
    return d->mPixmap;
}

void GravatarResolvUrlJob::setSize(int size)
{
    if (size <= 0) {
        size = 80;
    } else if (size > 2048) {
        size = 2048;
    }
    d->mSize = size;
}

222
Hash GravatarResolvUrlJob::calculatedHash() const
223
224
225
226
{
    return d->mCalculatedHash;
}

Laurent Montel's avatar
Laurent Montel committed
227
QUrl GravatarResolvUrlJob::createUrl(bool useLibravatar)
228
229
{
    QUrl url;
230
    d->mCalculatedHash = Hash();
231
232
233
234
235
    if (!canStart()) {
        return url;
    }
    QUrlQuery query;
    if (!d->mUseDefaultPixmap) {
Laurent Montel's avatar
Laurent Montel committed
236
        // Add ?d=404
237
238
239
240
241
        query.addQueryItem(QStringLiteral("d"), QStringLiteral("404"));
    }
    if (d->mSize != 80) {
        query.addQueryItem(QStringLiteral("s"), QString::number(d->mSize));
    }
242
    url.setScheme(QStringLiteral("https"));
Laurent Montel's avatar
Laurent Montel committed
243
244
245
246
247
    if (useLibravatar) {
        url.setHost(QStringLiteral("seccdn.libravatar.org"));
    } else {
        url.setHost(QStringLiteral("secure.gravatar.com"));
    }
Laurent Montel's avatar
Laurent Montel committed
248
    d->mCalculatedHash = calculateHash();
249
    url.setPath(QLatin1String("/avatar/") + d->mCalculatedHash.hexString());
250
251
252
    url.setQuery(query);
    return url;
}
253

254
bool GravatarResolvUrlJob::cacheLookup(const Hash &hash)
255
256
257
{
    bool haveStoredPixmap = false;
    const QPixmap pix = GravatarCache::self()->loadGravatarPixmap(hash, haveStoredPixmap);
258
    if (haveStoredPixmap && !pix.isNull()) { // we know a Gravatar for this hash
259
260
261
262
263
264
        d->mPixmap = pix;
        d->mHasGravatar = true;
        Q_EMIT finished(this);
        deleteLater();
        return true;
    }
265
    return haveStoredPixmap;
266
}