Messagelib

checkphishingurljob.cpp
1 /*
2  SPDX-FileCopyrightText: 2016-2023 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 <QUrlQuery>
12 #include <webengineviewer_debug.h>
13 using namespace WebEngineViewer;
14 
15 WEBENGINEVIEWER_EXPORT bool webengineview_useCompactJson = true;
16 
17 class WebEngineViewer::CheckPhishingUrlJobPrivate
18 {
19 public:
20  explicit CheckPhishingUrlJobPrivate(CheckPhishingUrlJob *q)
21  : mNetworkAccessManager(new QNetworkAccessManager(q))
22  {
23  }
24 
25  QUrl mUrl;
26  QNetworkAccessManager *const mNetworkAccessManager;
27 };
28 
29 CheckPhishingUrlJob::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 
41 CheckPhishingUrlJob::~CheckPhishingUrlJob() = default;
42 
43 void 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 
49 void 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 == QLatin1String("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 
89 void CheckPhishingUrlJob::slotCheckUrlFinished(QNetworkReply *reply)
90 {
91  parse(reply->readAll());
92  reply->deleteLater();
93  deleteLater();
94 }
95 
96 void CheckPhishingUrlJob::setUrl(const QUrl &url)
97 {
98  d->mUrl = url;
99 }
100 
101 QByteArray 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 
131 void 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, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::errorOccurred), this, &CheckPhishingUrlJob::slotError);
153  } else {
154  Q_EMIT result(WebEngineViewer::CheckPhishingUrlUtil::InvalidUrl, d->mUrl);
155  deleteLater();
156  }
157 }
158 
159 void CheckPhishingUrlJob::slotError(QNetworkReply::NetworkError error)
160 {
161  auto reply = qobject_cast<QNetworkReply *>(sender());
162  qCWarning(WEBENGINEVIEWER_LOG) << " error " << error << " error string : " << reply->errorString();
163  reply->deleteLater();
164  deleteLater();
165 }
166 
167 bool CheckPhishingUrlJob::canStart() const
168 {
169  return d->mUrl.isValid();
170 }
QString errorString() const const
void errorOccurred(QNetworkReply::NetworkError code)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
Q_EMITQ_EMIT
bool isNull() const const
QVariant toVariant() const const
QObject * sender() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
void finished(QNetworkReply *reply)
void deleteLater()
char * toString(const T &value)
bool isEmpty() const const
void sslErrors(QNetworkReply *reply, const QList< QSslError > &errors)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QMap< QString, QVariant > toMap() const const
void setQuery(const QString &query, QUrl::ParsingMode mode)
void ignoreSslErrors(const QList< QSslError > &errors)
QByteArray toJson() const const
QByteArray readAll()
QJsonDocument fromVariant(const QVariant &variant)
QFuture< void > map(Sequence &sequence, MapFunctor function)
The CheckPhishingUrlJob class.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Mar 24 2023 04:08:30 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.