File indexing completed on 2024-05-12 04:42:47
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "gbfsservice.h" 0008 #include "datatypes/json_p.h" 0009 0010 #include <QCryptographicHash> 0011 #include <QDebug> 0012 #include <QDirIterator> 0013 #include <QFile> 0014 #include <QJsonDocument> 0015 #include <QJsonObject> 0016 #include <QStandardPaths> 0017 0018 using namespace KPublicTransport; 0019 0020 void GBFSService::generateSystemId() 0021 { 0022 if (discoveryUrl.isEmpty()) { 0023 return; 0024 } 0025 systemId = QString::fromUtf8(QCryptographicHash::hash(discoveryUrl.toString().toUtf8(), QCryptographicHash::Sha1).toBase64(QByteArray::Base64UrlEncoding)); 0026 } 0027 0028 QJsonObject GBFSService::toJson(const GBFSService &service) 0029 { 0030 return Json::toJson(service); 0031 } 0032 0033 GBFSService GBFSService::fromJson(const QJsonObject &obj) 0034 { 0035 return Json::fromJson<GBFSService>(obj); 0036 } 0037 0038 0039 std::vector<GBFSService> GBFSServiceRepository::m_services; 0040 0041 static QString basePath() 0042 { 0043 return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.kpublictransport/gbfs/services/"); 0044 } 0045 0046 const std::vector<GBFSService>& GBFSServiceRepository::services() 0047 { 0048 if (m_services.empty()) { 0049 load(); 0050 } 0051 return m_services; 0052 } 0053 0054 void GBFSServiceRepository::store(const GBFSService &service) 0055 { 0056 if (service.systemId.isEmpty() || service.systemId.contains(QLatin1String("..")) || service.systemId.contains(QLatin1Char('/'))) { 0057 qWarning() << "invalid service id:" << service.systemId << service.discoveryUrl; 0058 return; 0059 } 0060 0061 const auto base = basePath(); 0062 QDir().mkpath(base); 0063 const QString fileName = base + service.systemId + QLatin1String(".json"); 0064 QFile f(fileName); 0065 if (!f.open(QFile::WriteOnly)) { 0066 qWarning() << f.errorString() << f.fileName(); 0067 return; 0068 } 0069 0070 f.write(QJsonDocument(GBFSService::toJson(service)).toJson(QJsonDocument::Compact)); 0071 } 0072 0073 void GBFSServiceRepository::load() 0074 { 0075 QDirIterator it(basePath(), QDir::Files); 0076 while (it.hasNext()) { 0077 auto n = it.next(); 0078 if (!n.endsWith(QLatin1String(".json"))) { 0079 continue; 0080 } 0081 0082 QFile f(n); 0083 if (!f.open(QFile::ReadOnly)) { 0084 qWarning() << f.errorString() << f.fileName(); 0085 continue; 0086 } 0087 0088 auto service = GBFSService::fromJson(QJsonDocument::fromJson(f.readAll()).object()); 0089 if (service.systemId.isEmpty()) { 0090 continue; 0091 } 0092 m_services.push_back(std::move(service)); 0093 } 0094 std::sort(m_services.begin(), m_services.end(), [](const auto &lhs, const auto &rhs) { return lhs.systemId < rhs.systemId; }); 0095 0096 // load missing ones from the initial built-in list 0097 QFile f(QStringLiteral(":/org.kde.kpublictransport/gbfs/gbfs-feeds.json")); 0098 if (!f.open(QFile::ReadOnly)) { 0099 qWarning() << f.errorString(); 0100 return; 0101 } 0102 0103 const auto array = QJsonDocument::fromJson(f.readAll()).array(); 0104 m_services.reserve(array.size()); 0105 const auto endIdx = m_services.size(); 0106 for (const auto &v : array) { 0107 const auto s = GBFSService::fromJson(v.toObject()); 0108 const auto it = std::lower_bound(m_services.begin(), m_services.begin() + endIdx, s, [](const auto &lhs, const auto &rhs) { return lhs.systemId < rhs.systemId; }); 0109 if (it != (m_services.begin() + endIdx) && (*it).systemId == s.systemId) { 0110 continue; 0111 } 0112 m_services.push_back(std::move(s)); 0113 } 0114 }