You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

179 lines
6.1 KiB

/*
SPDX-FileCopyrightText: 2009 Petri Damstén <damu@iki.fi>
Original Implementation:
SPDX-FileCopyrightText: 2009 Andrew Coles <andrew.coles@yahoo.co.uk>
Extension to iplocationtools engine:
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "location_ip.h"
#include "geolocdebug.h"
#include <KSharedConfig>
#include <NetworkManagerQt/Manager>
#include <NetworkManagerQt/WirelessDevice>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
class Ip::Private : public QObject
{
Q_OBJECT
public:
Private(Ip *q)
: q(q)
{
m_nam.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
m_nam.setStrictTransportSecurityEnabled(true);
m_nam.enableStrictTransportSecurityStore(true,
QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/plasmashell/hsts/"));
}
void readGeoLocation(QNetworkReply *reply)
{
m_geoLocationResolved = true;
if (reply->error()) {
qCCritical(DATAENGINE_GEOLOCATION) << "error: " << reply->errorString();
checkUpdateData();
return;
}
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
auto accuracyIt = json.find(QStringLiteral("accuracy"));
if (accuracyIt != json.end()) {
m_data[QStringLiteral("accuracy")] = (*accuracyIt).toDouble();
} else {
m_data[QStringLiteral("accuracy")] = 40000;
}
auto locationIt = json.find(QStringLiteral("location"));
if (locationIt != json.end()) {
QJsonObject location = (*locationIt).toObject();
m_data[QStringLiteral("latitude")] = location.value(QStringLiteral("lat")).toDouble();
m_data[QStringLiteral("longitude")] = location.value(QStringLiteral("lng")).toDouble();
}
checkUpdateData();
}
void clear()
{
m_countryResolved = false;
m_geoLocationResolved = false;
m_data.clear();
}
void readCountry(QNetworkReply *reply)
{
m_countryResolved = true;
if (reply->error()) {
qCCritical(DATAENGINE_GEOLOCATION) << "error: " << reply->errorString();
checkUpdateData();
return;
}
const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object();
m_data[QStringLiteral("country")] = json.value(QStringLiteral("country_name")).toString();
m_data[QStringLiteral("country code")] = json.value(QStringLiteral("country_code")).toString();
checkUpdateData();
}
void checkUpdateData()
{
if (!m_countryResolved || !m_geoLocationResolved) {
return;
}
q->setData(m_data);
}
Ip *q;
bool m_countryResolved = false;
bool m_geoLocationResolved = false;
Plasma::DataEngine::Data m_data;
QNetworkAccessManager m_nam;
};
Ip::Ip(QObject *parent, const QVariantList &args)
: GeolocationProvider(parent, args)
, d(new Private(this))
{
setUpdateTriggers(SourceEvent | NetworkConnected);
}
Ip::~Ip()
{
delete d;
}
static QJsonArray accessPoints()
{
QJsonArray wifiAccessPoints;
const KConfigGroup config = KSharedConfig::openConfig()->group(QStringLiteral("org.kde.plasma.geolocation.ip"));
if (!NetworkManager::isWirelessEnabled() || !config.readEntry("Wifi", false)) {
return wifiAccessPoints;
}
for (const auto &device : NetworkManager::networkInterfaces()) {
QSharedPointer<NetworkManager::WirelessDevice> wifi = qSharedPointerDynamicCast<NetworkManager::WirelessDevice>(device);
if (!wifi) {
continue;
}
for (const auto &network : wifi->networks()) {
const QString &ssid = network->ssid();
if (ssid.isEmpty() || ssid.endsWith(QLatin1String("_nomap"))) {
// skip hidden SSID and networks with "_nomap"
continue;
}
for (const auto &accessPoint : network->accessPoints()) {
wifiAccessPoints.append(QJsonObject{{QStringLiteral("macAddress"), accessPoint->hardwareAddress()}});
}
}
}
return wifiAccessPoints;
}
void Ip::update()
{
d->clear();
if (!NetworkManager::isNetworkingEnabled()) {
setData(Plasma::DataEngine::Data());
return;
}
const QJsonArray wifiAccessPoints = accessPoints();
QJsonObject request;
if (wifiAccessPoints.count() >= 2) {
request.insert(QStringLiteral("wifiAccessPoints"), wifiAccessPoints);
}
const QByteArray postData = QJsonDocument(request).toJson(QJsonDocument::Compact);
const QString apiKey = QStringLiteral("60e8eae6-3988-4ada-ad48-2cfddddf216b");
qCDebug(DATAENGINE_GEOLOCATION) << "Fetching https://location.services.mozilla.com/v1/geolocate";
QNetworkRequest locationRequest(QUrl(QStringLiteral("https://location.services.mozilla.com/v1/geolocate?key=%1").arg(apiKey)));
locationRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
QNetworkReply *locationReply = d->m_nam.post(locationRequest, postData);
connect(locationReply, &QNetworkReply::finished, this, [this, locationReply] {
locationReply->deleteLater();
d->readGeoLocation(locationReply);
});
qCDebug(DATAENGINE_GEOLOCATION) << "Fetching https://location.services.mozilla.com/v1/country";
QNetworkRequest countryRequest(QUrl(QStringLiteral("https://location.services.mozilla.com/v1/country?key=%1").arg(apiKey)));
countryRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
QNetworkReply *countryReply = d->m_nam.post(countryRequest, postData);
connect(countryReply, &QNetworkReply::finished, this, [this, countryReply] {
countryReply->deleteLater();
d->readCountry(countryReply);
});
}
K_PLUGIN_CLASS_WITH_JSON(Ip, "plasma-geolocation-ip.json")
#include "location_ip.moc"