Messagelib

checkphishingurljob.cpp
1/*
2 SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "checkphishingurljob.h"
8#include <PimCommon/NetworkManager>
9#include <QJsonDocument>
10#include <QNetworkAccessManager>
11#include <QUrlQuery>
12#include <webengineviewer_debug.h>
13using namespace WebEngineViewer;
14
15WEBENGINEVIEWER_EXPORT bool webengineview_useCompactJson = true;
16
17class WebEngineViewer::CheckPhishingUrlJobPrivate
18{
19public:
20 explicit CheckPhishingUrlJobPrivate(CheckPhishingUrlJob *q)
21 : mNetworkAccessManager(new QNetworkAccessManager(q))
22 {
23 }
24
25 QUrl mUrl;
26 QNetworkAccessManager *const mNetworkAccessManager;
27};
28
29CheckPhishingUrlJob::CheckPhishingUrlJob(QObject *parent)
30 : QObject(parent)
31 , d(new WebEngineViewer::CheckPhishingUrlJobPrivate(this))
32{
33 d->mNetworkAccessManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
34 d->mNetworkAccessManager->setStrictTransportSecurityEnabled(true);
35 d->mNetworkAccessManager->enableStrictTransportSecurityStore(true);
36
37 connect(d->mNetworkAccessManager, &QNetworkAccessManager::finished, this, &CheckPhishingUrlJob::slotCheckUrlFinished);
38 connect(d->mNetworkAccessManager, &QNetworkAccessManager::sslErrors, this, &CheckPhishingUrlJob::slotSslErrors);
39}
40
41CheckPhishingUrlJob::~CheckPhishingUrlJob() = default;
42
43void CheckPhishingUrlJob::slotSslErrors(QNetworkReply *reply, const QList<QSslError> &error)
44{
45 qCDebug(WEBENGINEVIEWER_LOG) << " void CheckPhishingUrlJob::slotSslErrors(QNetworkReply *reply, const QList<QSslError> &error)" << error.count();
46 reply->ignoreSslErrors(error);
47}
48
49void CheckPhishingUrlJob::parse(const QByteArray &replyStr)
50{
51 QJsonDocument document = QJsonDocument::fromJson(replyStr);
52 if (document.isNull()) {
53 Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::Unknown, d->mUrl);
54 } else {
55 const QVariantMap answer = document.toVariant().toMap();
56 if (answer.isEmpty()) {
57 Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::Ok, d->mUrl);
58 return;
59 } else {
60 const QVariantList info = answer.value(QStringLiteral("matches")).toList();
61 if (info.count() == 1) {
62 const QVariantMap map = info.at(0).toMap();
63 const QString threatTypeStr = map[QStringLiteral("threatType")].toString();
64 const QString cacheDuration = map[QStringLiteral("cacheDuration")].toString();
65 uint verifyCacheAfterThisTime = 0;
66 if (!cacheDuration.isEmpty()) {
67 double cacheDurationValue = WebEngineViewer::CheckPhishingUrlUtil::convertToSecond(cacheDuration);
68 if (cacheDurationValue > 0) {
69 verifyCacheAfterThisTime = WebEngineViewer::CheckPhishingUrlUtil::refreshingCacheAfterThisTime(cacheDurationValue);
70 }
71 }
72 if (threatTypeStr == QLatin1StringView("MALWARE")) {
73 const QVariantMap urlMap = map[QStringLiteral("threat")].toMap();
74 if (urlMap.count() == 1) {
75 if (urlMap[QStringLiteral("url")].toString() == d->mUrl.toString()) {
76 Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::MalWare, d->mUrl, verifyCacheAfterThisTime);
77 return;
78 }
79 }
80 } else {
81 qCWarning(WEBENGINEVIEWER_LOG) << " CheckPhishingUrlJob::parse threatTypeStr : " << threatTypeStr;
82 }
83 }
84 Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::Unknown, d->mUrl);
85 }
86 }
87}
88
89void CheckPhishingUrlJob::slotCheckUrlFinished(QNetworkReply *reply)
90{
91 parse(reply->readAll());
92 reply->deleteLater();
94}
95
96void CheckPhishingUrlJob::setUrl(const QUrl &url)
97{
98 d->mUrl = url;
99}
100
101QByteArray CheckPhishingUrlJob::jsonRequest() const
102{
103 QVariantMap clientMap;
104 QVariantMap map;
105
106 clientMap.insert(QStringLiteral("clientId"), QStringLiteral("KDE"));
107 clientMap.insert(QStringLiteral("clientVersion"), CheckPhishingUrlUtil::versionApps());
108 map.insert(QStringLiteral("client"), clientMap);
109
110 QVariantMap threatMap;
111 const QVariantList platformList = {QStringLiteral("WINDOWS")};
112 threatMap.insert(QStringLiteral("platformTypes"), platformList);
113
114 const QVariantList threatTypesList = {QStringLiteral("MALWARE")};
115 threatMap.insert(QStringLiteral("threatTypes"), threatTypesList);
116 const QVariantList threatEntryTypesList = {QStringLiteral("URL")};
117 threatMap.insert(QStringLiteral("threatEntryTypes"), threatEntryTypesList);
118 QVariantList threatEntriesList;
119 QVariantMap urlMap;
120 urlMap.insert(QStringLiteral("url"), d->mUrl.toString());
121 threatEntriesList.append(urlMap);
122 threatMap.insert(QStringLiteral("threatEntries"), threatEntriesList);
123
124 map.insert(QStringLiteral("threatInfo"), threatMap);
125
126 const QJsonDocument postData = QJsonDocument::fromVariant(map);
127 const QByteArray baPostData = postData.toJson(webengineview_useCompactJson ? QJsonDocument::Compact : QJsonDocument::Indented);
128 return baPostData;
129}
130
131void CheckPhishingUrlJob::start()
132{
133 if (!PimCommon::NetworkManager::self()->isOnline()) {
134 Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::BrokenNetwork, d->mUrl);
135 deleteLater();
136 } else if (canStart()) {
138 query.addQueryItem(QStringLiteral("key"), WebEngineViewer::CheckPhishingUrlUtil::apiKey());
139 QUrl safeUrl = QUrl(QStringLiteral("https://safebrowsing.googleapis.com/v4/threatMatches:find"));
140 safeUrl.setQuery(query);
141 QNetworkRequest request(safeUrl);
142 request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
143
144 const QByteArray baPostData = jsonRequest();
145 qCDebug(WEBENGINEVIEWER_LOG) << " postData.toJson()" << baPostData;
146 Q_EMIT debugJson(baPostData);
147 // curl -H "Content-Type: application/json" -X POST -d
148 // '{"client":{"clientId":"KDE","clientVersion":"5.4.0"},"threatInfo":{"platformTypes":["WINDOWS"],"threatEntries":[{"url":"http://www.kde.org"}],"threatEntryTypes":["URL"],"threatTypes":["MALWARE"]}}'
149 // https://safebrowsing.googleapis.com/v4/threatMatches:find?key=AIzaSyBS62pXATjabbH2RM_jO2EzDg1mTMHlnyo
150
151 QNetworkReply *reply = d->mNetworkAccessManager->post(request, baPostData);
152 connect(reply, &QNetworkReply::errorOccurred, this, &CheckPhishingUrlJob::slotError);
153 } else {
154 Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::InvalidUrl, d->mUrl);
155 deleteLater();
156 }
157}
158
159void CheckPhishingUrlJob::slotError(QNetworkReply::NetworkError error)
160{
162 qCWarning(WEBENGINEVIEWER_LOG) << " error " << error << " error string : " << reply->errorString();
163 reply->deleteLater();
164 deleteLater();
165}
166
167bool CheckPhishingUrlJob::canStart() const
168{
169 return d->mUrl.isValid();
170}
171
172#include "moc_checkphishingurljob.cpp"
The CheckPhishingUrlJob class.
std::optional< QSqlQuery > query(const QString &queryStatement)
char * toString(const EngineQuery &query)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString errorString() const const
QByteArray readAll()
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonDocument fromVariant(const QVariant &variant)
bool isNull() const const
QByteArray toJson(JsonFormat format) const const
QVariant toVariant() const const
void finished(QNetworkReply *reply)
void errorOccurred(QNetworkReply::NetworkError code)
virtual void ignoreSslErrors()
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
T qobject_cast(QObject *object)
QObject * sender() const const
bool isEmpty() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setQuery(const QString &query, ParsingMode mode)
QMap< QString, QVariant > toMap() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:59:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.