Plasma5Support

location_ip.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Petri Damstén <damu@iki.fi>
3
4 Original Implementation:
5 SPDX-FileCopyrightText: 2009 Andrew Coles <andrew.coles@yahoo.co.uk>
6
7 Extension to iplocationtools engine:
8 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11*/
12
13#include "location_ip.h"
14#include "geolocdebug.h"
15#include <KSharedConfig>
16#include <NetworkManagerQt/Manager>
17#include <NetworkManagerQt/WirelessDevice>
18#include <QJsonArray>
19#include <QJsonDocument>
20#include <QJsonObject>
21#include <QNetworkAccessManager>
22#include <QNetworkReply>
23#include <QUrl>
24
25class Ip::Private : public QObject
26{
28public:
29 Private(Ip *q)
30 : q(q)
31 {
36 }
37
38 void readGeoLocation(QNetworkReply *reply)
39 {
40 m_geoLocationResolved = true;
41 if (reply->error()) {
42 qCCritical(DATAENGINE_GEOLOCATION) << "error: " << reply->errorString();
43 checkUpdateData();
44 return;
45 }
46 const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
47
48 auto accuracyIt = json.find(QStringLiteral("accuracy"));
49 if (accuracyIt != json.end()) {
50 m_data[QStringLiteral("accuracy")] = (*accuracyIt).toDouble();
51 } else {
52 m_data[QStringLiteral("accuracy")] = 40000;
53 }
54
55 auto locationIt = json.find(QStringLiteral("location"));
56 if (locationIt != json.end()) {
57 QJsonObject location = (*locationIt).toObject();
58 m_data[QStringLiteral("latitude")] = location.value(QStringLiteral("lat")).toDouble();
59 m_data[QStringLiteral("longitude")] = location.value(QStringLiteral("lng")).toDouble();
60 }
61 checkUpdateData();
62 }
63
64 void clear()
65 {
66 m_countryResolved = false;
67 m_geoLocationResolved = false;
68 m_data.clear();
69 }
70
71 void readCountry(QNetworkReply *reply)
72 {
73 m_countryResolved = true;
74 if (reply->error()) {
75 qCCritical(DATAENGINE_GEOLOCATION) << "error: " << reply->errorString();
76 checkUpdateData();
77 return;
78 }
79
80 const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
81
82 m_data[QStringLiteral("country")] = json.value(QStringLiteral("country_name")).toString();
83 m_data[QStringLiteral("country code")] = json.value(QStringLiteral("country_code")).toString();
84
85 checkUpdateData();
86 }
87
88 void checkUpdateData()
89 {
90 if (!m_countryResolved || !m_geoLocationResolved) {
91 return;
92 }
93 q->setData(m_data);
94 }
95
96 Ip *q;
97 bool m_countryResolved = false;
98 bool m_geoLocationResolved = false;
101};
102
103Ip::Ip(QObject *parent)
104 : GeolocationProvider(parent)
105 , d(new Private(this))
106{
107 setUpdateTriggers(SourceEvent | NetworkConnected);
108}
109
110Ip::~Ip()
111{
112 delete d;
113}
114
115static QJsonArray accessPoints()
116{
117 QJsonArray wifiAccessPoints;
118 const KConfigGroup config = KSharedConfig::openConfig()->group(QStringLiteral("org.kde.plasma.geolocation.ip"));
119 if (!NetworkManager::isWirelessEnabled() || !config.readEntry("Wifi", false)) {
120 return wifiAccessPoints;
121 }
122 for (const auto &device : NetworkManager::networkInterfaces()) {
123 QSharedPointer<NetworkManager::WirelessDevice> wifi = qSharedPointerDynamicCast<NetworkManager::WirelessDevice>(device);
124 if (!wifi) {
125 continue;
126 }
127 for (const auto &network : wifi->networks()) {
128 const QString &ssid = network->ssid();
129 if (ssid.isEmpty() || ssid.endsWith(QLatin1String("_nomap"))) {
130 // skip hidden SSID and networks with "_nomap"
131 continue;
132 }
133 for (const auto &accessPoint : network->accessPoints()) {
134 wifiAccessPoints.append(QJsonObject{{QStringLiteral("macAddress"), accessPoint->hardwareAddress()}});
135 }
136 }
137 }
138 return wifiAccessPoints;
139}
140
141void Ip::update()
142{
143 d->clear();
146 return;
147 }
148 const QJsonArray wifiAccessPoints = accessPoints();
149 QJsonObject request;
150 if (wifiAccessPoints.count() >= 2) {
151 request.insert(QStringLiteral("wifiAccessPoints"), wifiAccessPoints);
152 }
153 const QByteArray postData = QJsonDocument(request).toJson(QJsonDocument::Compact);
154 const QString apiKey = QStringLiteral("60e8eae6-3988-4ada-ad48-2cfddddf216b");
155
156 qCDebug(DATAENGINE_GEOLOCATION) << "Fetching https://location.services.mozilla.com/v1/geolocate";
157 QNetworkRequest locationRequest(QUrl(QStringLiteral("https://location.services.mozilla.com/v1/geolocate?key=%1").arg(apiKey)));
158 locationRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
159 QNetworkReply *locationReply = d->m_nam.post(locationRequest, postData);
160
161 connect(locationReply, &QNetworkReply::finished, this, [this, locationReply] {
162 locationReply->deleteLater();
163 d->readGeoLocation(locationReply);
164 });
165
166 qCDebug(DATAENGINE_GEOLOCATION) << "Fetching https://location.services.mozilla.com/v1/country";
167 QNetworkRequest countryRequest(QUrl(QStringLiteral("https://location.services.mozilla.com/v1/country?key=%1").arg(apiKey)));
168 countryRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
169 QNetworkReply *countryReply = d->m_nam.post(countryRequest, postData);
170
171 connect(countryReply, &QNetworkReply::finished, this, [this, countryReply] {
172 countryReply->deleteLater();
173 d->readCountry(countryReply);
174 });
175}
176
178
179#include "location_ip.moc"
KConfigGroup group(const QString &group)
QString readEntry(const char *key, const char *aDefault=nullptr) const
#define K_PLUGIN_CLASS(classname)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QVariant location(const QVariant &res)
NETWORKMANAGERQT_EXPORT bool isWirelessEnabled()
NETWORKMANAGERQT_EXPORT bool isNetworkingEnabled()
NETWORKMANAGERQT_EXPORT Device::List networkInterfaces()
QString errorString() const const
QByteArray readAll()
void append(const QJsonValue &value)
qsizetype count() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
iterator end()
iterator find(QLatin1StringView key)
iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
void clear()
void enableStrictTransportSecurityStore(bool enabled, const QString &storeDir)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
void setStrictTransportSecurityEnabled(bool enabled)
NetworkError error() const const
Q_OBJECTQ_OBJECT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString writableLocation(StandardLocation type)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
T value() 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:38 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.