File indexing completed on 2024-05-12 05:37:18
0001 /* 0002 SPDX-FileCopyrightText: 2009 Petri Damstén <damu@iki.fi> 0003 0004 Original Implementation: 0005 SPDX-FileCopyrightText: 2009 Andrew Coles <andrew.coles@yahoo.co.uk> 0006 0007 Extension to iplocationtools engine: 0008 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include "location_ip.h" 0014 #include "geolocdebug.h" 0015 #include <KSharedConfig> 0016 #include <NetworkManagerQt/Manager> 0017 #include <NetworkManagerQt/WirelessDevice> 0018 #include <QJsonArray> 0019 #include <QJsonDocument> 0020 #include <QJsonObject> 0021 #include <QNetworkAccessManager> 0022 #include <QNetworkReply> 0023 #include <QUrl> 0024 0025 class Ip::Private : public QObject 0026 { 0027 Q_OBJECT 0028 public: 0029 Private(Ip *q) 0030 : q(q) 0031 { 0032 m_nam.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); 0033 m_nam.setStrictTransportSecurityEnabled(true); 0034 m_nam.enableStrictTransportSecurityStore(true, 0035 QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/plasmashell/hsts/")); 0036 } 0037 0038 void readGeoLocation(QNetworkReply *reply) 0039 { 0040 m_geoLocationResolved = true; 0041 if (reply->error()) { 0042 qCCritical(DATAENGINE_GEOLOCATION) << "error: " << reply->errorString(); 0043 checkUpdateData(); 0044 return; 0045 } 0046 const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object(); 0047 0048 auto accuracyIt = json.find(QStringLiteral("accuracy")); 0049 if (accuracyIt != json.end()) { 0050 m_data[QStringLiteral("accuracy")] = (*accuracyIt).toDouble(); 0051 } else { 0052 m_data[QStringLiteral("accuracy")] = 40000; 0053 } 0054 0055 auto locationIt = json.find(QStringLiteral("location")); 0056 if (locationIt != json.end()) { 0057 QJsonObject location = (*locationIt).toObject(); 0058 m_data[QStringLiteral("latitude")] = location.value(QStringLiteral("lat")).toDouble(); 0059 m_data[QStringLiteral("longitude")] = location.value(QStringLiteral("lng")).toDouble(); 0060 } 0061 checkUpdateData(); 0062 } 0063 0064 void clear() 0065 { 0066 m_countryResolved = false; 0067 m_geoLocationResolved = false; 0068 m_data.clear(); 0069 } 0070 0071 void readCountry(QNetworkReply *reply) 0072 { 0073 m_countryResolved = true; 0074 if (reply->error()) { 0075 qCCritical(DATAENGINE_GEOLOCATION) << "error: " << reply->errorString(); 0076 checkUpdateData(); 0077 return; 0078 } 0079 0080 const QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object(); 0081 0082 m_data[QStringLiteral("country")] = json.value(QStringLiteral("country_name")).toString(); 0083 m_data[QStringLiteral("country code")] = json.value(QStringLiteral("country_code")).toString(); 0084 0085 checkUpdateData(); 0086 } 0087 0088 void checkUpdateData() 0089 { 0090 if (!m_countryResolved || !m_geoLocationResolved) { 0091 return; 0092 } 0093 q->setData(m_data); 0094 } 0095 0096 Ip *q; 0097 bool m_countryResolved = false; 0098 bool m_geoLocationResolved = false; 0099 Plasma5Support::DataEngine::Data m_data; 0100 QNetworkAccessManager m_nam; 0101 }; 0102 0103 Ip::Ip(QObject *parent) 0104 : GeolocationProvider(parent) 0105 , d(new Private(this)) 0106 { 0107 setUpdateTriggers(SourceEvent | NetworkConnected); 0108 } 0109 0110 Ip::~Ip() 0111 { 0112 delete d; 0113 } 0114 0115 static QJsonArray accessPoints() 0116 { 0117 QJsonArray wifiAccessPoints; 0118 const KConfigGroup config = KSharedConfig::openConfig()->group(QStringLiteral("org.kde.plasma.geolocation.ip")); 0119 if (!NetworkManager::isWirelessEnabled() || !config.readEntry("Wifi", false)) { 0120 return wifiAccessPoints; 0121 } 0122 for (const auto &device : NetworkManager::networkInterfaces()) { 0123 QSharedPointer<NetworkManager::WirelessDevice> wifi = qSharedPointerDynamicCast<NetworkManager::WirelessDevice>(device); 0124 if (!wifi) { 0125 continue; 0126 } 0127 for (const auto &network : wifi->networks()) { 0128 const QString &ssid = network->ssid(); 0129 if (ssid.isEmpty() || ssid.endsWith(QLatin1String("_nomap"))) { 0130 // skip hidden SSID and networks with "_nomap" 0131 continue; 0132 } 0133 for (const auto &accessPoint : network->accessPoints()) { 0134 wifiAccessPoints.append(QJsonObject{{QStringLiteral("macAddress"), accessPoint->hardwareAddress()}}); 0135 } 0136 } 0137 } 0138 return wifiAccessPoints; 0139 } 0140 0141 void Ip::update() 0142 { 0143 d->clear(); 0144 if (!NetworkManager::isNetworkingEnabled()) { 0145 setData(Plasma5Support::DataEngine::Data()); 0146 return; 0147 } 0148 const QJsonArray wifiAccessPoints = accessPoints(); 0149 QJsonObject request; 0150 if (wifiAccessPoints.count() >= 2) { 0151 request.insert(QStringLiteral("wifiAccessPoints"), wifiAccessPoints); 0152 } 0153 const QByteArray postData = QJsonDocument(request).toJson(QJsonDocument::Compact); 0154 const QString apiKey = QStringLiteral("60e8eae6-3988-4ada-ad48-2cfddddf216b"); 0155 0156 qCDebug(DATAENGINE_GEOLOCATION) << "Fetching https://location.services.mozilla.com/v1/geolocate"; 0157 QNetworkRequest locationRequest(QUrl(QStringLiteral("https://location.services.mozilla.com/v1/geolocate?key=%1").arg(apiKey))); 0158 locationRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0159 QNetworkReply *locationReply = d->m_nam.post(locationRequest, postData); 0160 0161 connect(locationReply, &QNetworkReply::finished, this, [this, locationReply] { 0162 locationReply->deleteLater(); 0163 d->readGeoLocation(locationReply); 0164 }); 0165 0166 qCDebug(DATAENGINE_GEOLOCATION) << "Fetching https://location.services.mozilla.com/v1/country"; 0167 QNetworkRequest countryRequest(QUrl(QStringLiteral("https://location.services.mozilla.com/v1/country?key=%1").arg(apiKey))); 0168 countryRequest.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0169 QNetworkReply *countryReply = d->m_nam.post(countryRequest, postData); 0170 0171 connect(countryReply, &QNetworkReply::finished, this, [this, countryReply] { 0172 countryReply->deleteLater(); 0173 d->readCountry(countryReply); 0174 }); 0175 } 0176 0177 K_PLUGIN_CLASS(Ip) 0178 0179 #include "location_ip.moc"