Messagelib

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

KDE's Doxygen guidelines are available online.