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 "gbfsstore.h" 0008 #include "logging.h" 0009 0010 #include <QDateTime> 0011 #include <QDebug> 0012 #include <QDir> 0013 #include <QDirIterator> 0014 #include <QFile> 0015 #include <QFileInfo> 0016 #include <QJsonDocument> 0017 #include <QJsonObject> 0018 #include <QStandardPaths> 0019 #include <QUrl> 0020 0021 #include <cassert> 0022 #include <chrono> 0023 0024 using namespace KPublicTransport; 0025 0026 struct { 0027 const std::chrono::seconds ttl; 0028 } static constexpr const file_ttl_map[] = { 0029 { std::chrono::hours(24 * 5) }, 0030 { std::chrono::hours(24 * 5) }, 0031 { std::chrono::hours(24 * 5) }, 0032 { std::chrono::minutes(5) }, 0033 { std::chrono::minutes(5) }, 0034 { std::chrono::hours(24 * 5) }, 0035 { std::chrono::hours(24 * 5) }, 0036 { std::chrono::hours(24 * 5) }, 0037 { std::chrono::hours(24 * 5) }, 0038 { std::chrono::hours(24 * 5) }, 0039 { std::chrono::hours(24 * 5) }, 0040 { std::chrono::hours(24 * 5) }, 0041 { std::chrono::hours(24 * 5) }, 0042 }; 0043 0044 static_assert((sizeof(file_ttl_map) / sizeof(file_ttl_map[0])) == GBFS::Unknown, ""); 0045 0046 GBFSStore::GBFSStore() = default; 0047 0048 GBFSStore::GBFSStore(const QString &systemId) 0049 : m_systemId(systemId) 0050 { 0051 } 0052 0053 GBFSStore::~GBFSStore() = default; 0054 0055 bool GBFSStore::isValid() const 0056 { 0057 return !m_systemId.isEmpty(); 0058 } 0059 0060 bool GBFSStore::hasData(GBFS::FileType type) const 0061 { 0062 const auto name = fileName(type); 0063 return QFile::exists(name); 0064 } 0065 0066 bool GBFSStore::hasCurrentData(GBFS::FileType type) const 0067 { 0068 const auto name = fileName(type); 0069 QFileInfo fi(name); 0070 return fi.exists() && fi.fileTime(QFile::FileModificationTime) >= QDateTime::currentDateTime(); 0071 } 0072 0073 void GBFSStore::storeData(GBFS::FileType type, const QJsonDocument &doc) 0074 { 0075 const auto name = fileName(type); 0076 QFile f(name); 0077 if (!f.open(QFile::WriteOnly)) { 0078 qWarning() << f.errorString() << f.fileName(); 0079 return; 0080 } 0081 f.write(doc.toJson(QJsonDocument::Compact)); 0082 f.close(); 0083 0084 // mtime changes need to be done without content changes to take effect 0085 const auto ttl = std::max(std::chrono::seconds(doc.object().value(QLatin1String("ttl")).toInt()), file_ttl_map[type].ttl); 0086 f.open(QFile::WriteOnly | QFile::Append); 0087 f.setFileTime(QDateTime::currentDateTimeUtc().addSecs(ttl.count()), QFile::FileModificationTime); 0088 f.close(); 0089 } 0090 0091 QJsonDocument GBFSStore::loadData(GBFS::FileType type) const 0092 { 0093 const auto name = fileName(type); 0094 QFile f(name); 0095 if (!f.open(QFile::ReadOnly)) { 0096 qWarning() << f.errorString() << f.fileName(); 0097 return {}; 0098 } 0099 return QJsonDocument::fromJson(f.readAll()); 0100 } 0101 0102 static void expireRecursive(const QString &path) 0103 { 0104 const auto now = QDateTime::currentDateTime(); 0105 QDirIterator it(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); 0106 while (it.hasNext()) { 0107 it.next(); 0108 0109 if (it.fileInfo().isDir()) { 0110 expireRecursive(it.filePath()); 0111 if (QDir(it.filePath()).isEmpty()) { 0112 qCDebug(Log) << "removing empty cache directory" << it.fileName(); 0113 QDir(path).rmdir(it.filePath()); 0114 } 0115 } else if (it.fileInfo().lastModified() < now) { 0116 qCDebug(Log) << "removing expired cache entry" << it.filePath(); 0117 QDir(path).remove(it.filePath()); 0118 } 0119 } 0120 } 0121 0122 void GBFSStore::expire() 0123 { 0124 expireRecursive(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.kpublictransport/gbfs/feeds/")); 0125 } 0126 0127 QString GBFSStore::fileName(GBFS::FileType type) const 0128 { 0129 assert(!m_systemId.isEmpty()); 0130 QString path = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/org.kde.kpublictransport/gbfs/feeds/") + m_systemId; 0131 QDir().mkpath(path); 0132 path += QLatin1Char('/') + QString::fromUtf8(GBFS::keyNameForType(type)) + QLatin1String(".json"); 0133 return path; 0134 }