/* SPDX-FileCopyrightText: 2009 Petri Damstén Original Implementation: SPDX-FileCopyrightText: 2009 Andrew Coles Extension to iplocationtools engine: SPDX-FileCopyrightText: 2015 Martin Gräßlin SPDX-License-Identifier: GPL-2.0-or-later */ #include "location_ip.h" #include "geolocdebug.h" #include #include #include #include #include #include #include #include #include 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; Plasma5Support::DataEngine::Data m_data; QNetworkAccessManager m_nam; }; Ip::Ip(QObject *parent) : GeolocationProvider(parent) , 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 wifi = qSharedPointerDynamicCast(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(Plasma5Support::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(Ip) #include "location_ip.moc"