Messagelib

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

KDE's Doxygen guidelines are available online.