File indexing completed on 2025-02-02 05:02:28
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "config-itinerary.h" 0008 #include "healthcertificatemanager.h" 0009 0010 #if HAVE_KHEALTHCERTIFICATE 0011 #include <KHealthCertificate/KHealthCertificateParser> 0012 #include <KHealthCertificate/KRecoveryCertificate> 0013 #include <KHealthCertificate/KTestCertificate> 0014 #include <KHealthCertificate/KVaccinationCertificate> 0015 #endif 0016 0017 #include <KLocalizedString> 0018 0019 #include <QDebug> 0020 #include <QDir> 0021 #include <QDirIterator> 0022 #include <QFile> 0023 #include <QStandardPaths> 0024 #include <QUuid> 0025 0026 HealthCertificateManager::HealthCertificateManager(QObject *parent) 0027 : QAbstractListModel(parent) 0028 { 0029 loadCertificates(); 0030 } 0031 0032 HealthCertificateManager::~HealthCertificateManager() = default; 0033 0034 bool HealthCertificateManager::isAvailable() 0035 { 0036 #if HAVE_KHEALTHCERTIFICATE 0037 return true; 0038 #else 0039 return false; 0040 #endif 0041 } 0042 0043 static QString basePath() 0044 { 0045 return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1StringView("/health-certificates/"); 0046 } 0047 0048 bool HealthCertificateManager::importCertificate(const QByteArray &rawData) 0049 { 0050 // check whether we know this certificate already 0051 for (const auto &c : m_certificates) { 0052 if (certificateRawData(c) == rawData) { 0053 return true; 0054 } 0055 } 0056 #if HAVE_KHEALTHCERTIFICATE 0057 CertData certData; 0058 certData.cert = KHealthCertificateParser::parse(rawData); 0059 if (certData.cert.isNull()) { 0060 return false; 0061 } 0062 0063 auto path = basePath(); 0064 QDir().mkpath(path); 0065 certData.name = QUuid::createUuid().toString(QUuid::WithoutBraces); 0066 path += QLatin1Char('/') + certData.name; 0067 0068 QFile f(path); 0069 if (!f.open(QFile::WriteOnly)) { 0070 qWarning() << f.errorString() << f.fileName(); 0071 return false; 0072 } 0073 f.write(rawData); 0074 f.close(); 0075 0076 const auto it = std::lower_bound(m_certificates.begin(), m_certificates.end(), certData, certLessThan); 0077 const auto row = std::distance(m_certificates.begin(), it); 0078 beginInsertRows({}, row, row); 0079 m_certificates.insert(it, std::move(certData)); 0080 endInsertRows(); 0081 Q_EMIT newCertificateLoaded(row); 0082 return true; 0083 #else 0084 return false; 0085 #endif 0086 } 0087 0088 void HealthCertificateManager::removeCertificate(int row) 0089 { 0090 beginRemoveRows({}, row, row); 0091 const auto it = m_certificates.begin() + row; 0092 QFile::remove(basePath() + QLatin1Char('/') + (*it).name); 0093 m_certificates.erase(it); 0094 endRemoveRows(); 0095 } 0096 0097 int HealthCertificateManager::rowCount(const QModelIndex& parent) const 0098 { 0099 if (parent.isValid()) { 0100 return 0; 0101 } 0102 return m_certificates.size(); 0103 } 0104 0105 QVariant HealthCertificateManager::data(const QModelIndex &index, int role) const 0106 { 0107 if (!checkIndex(index) || index.row() < 0) { 0108 return {}; 0109 } 0110 0111 const auto &v = m_certificates[index.row()]; 0112 switch (role) { 0113 case Qt::DisplayRole: 0114 #if HAVE_KHEALTHCERTIFICATE 0115 if (v.cert.userType() == qMetaTypeId<KVaccinationCertificate>()) { 0116 const auto cert = v.cert.value<KVaccinationCertificate>(); 0117 if (cert.dose() > 0 && cert.totalDoses() > 0) { 0118 return i18n("Vaccination %1/%2 (%3)", cert.dose(), cert.totalDoses(), cert.name()); 0119 } 0120 return i18n("Vaccination (%1)", cert.name()); 0121 } 0122 if (v.cert.userType() == qMetaTypeId<KTestCertificate>()) { 0123 const auto cert = v.cert.value<KTestCertificate>(); 0124 return i18n("Test %1 (%2)", QLocale().toString(cert.date().isValid() ? cert.date() : cert.certificateIssueDate().date(), QLocale::NarrowFormat), cert.name()); 0125 } 0126 if (v.cert.userType() == qMetaTypeId<KRecoveryCertificate>()) { 0127 const auto cert = v.cert.value<KRecoveryCertificate>(); 0128 return i18n("Recovery (%1)", cert.name()); 0129 } 0130 #endif 0131 return {}; 0132 case CertificateRole: 0133 return v.cert; 0134 case RawDataRole: 0135 return certificateRawData(v); 0136 case StorageIdRole: 0137 return v.name; 0138 } 0139 return {}; 0140 } 0141 0142 QHash<int, QByteArray> HealthCertificateManager::roleNames() const 0143 { 0144 auto rns = QAbstractListModel::roleNames(); 0145 rns.insert(CertificateRole, "certificate"); 0146 rns.insert(RawDataRole, "rawData"); 0147 rns.insert(StorageIdRole, "storageId"); 0148 return rns; 0149 } 0150 0151 void HealthCertificateManager::loadCertificates() 0152 { 0153 beginResetModel(); 0154 for (QDirIterator it(basePath(), QDir::Files); it.hasNext();) { 0155 QFile f(it.next()); 0156 if (!f.open(QFile::ReadOnly)) { 0157 qWarning() << f.errorString() << f.fileName(); 0158 continue; 0159 } 0160 0161 const auto rawData = f.readAll(); 0162 CertData certData; 0163 certData.name = it.fileName(); 0164 #if HAVE_KHEALTHCERTIFICATE 0165 certData.cert = KHealthCertificateParser::parse(rawData); 0166 #endif 0167 if (certData.cert.isNull()) { 0168 continue; 0169 } 0170 0171 m_certificates.push_back(std::move(certData)); 0172 } 0173 std::sort(m_certificates.begin(), m_certificates.end(), certLessThan); 0174 endResetModel(); 0175 } 0176 0177 QByteArray HealthCertificateManager::certificateRawData([[maybe_unused]] const CertData &certData) const 0178 { 0179 #if HAVE_KHEALTHCERTIFICATE 0180 if (certData.cert.userType() == qMetaTypeId<KVaccinationCertificate>()) { 0181 return certData.cert.value<KVaccinationCertificate>().rawData(); 0182 } 0183 if (certData.cert.userType() == qMetaTypeId<KTestCertificate>()) { 0184 return certData.cert.value<KTestCertificate>().rawData(); 0185 } 0186 if (certData.cert.userType() == qMetaTypeId<KRecoveryCertificate>()) { 0187 return certData.cert.value<KRecoveryCertificate>().rawData(); 0188 } 0189 #endif 0190 return {}; 0191 } 0192 0193 bool HealthCertificateManager::certLessThan(const CertData &lhs, const CertData &rhs) 0194 { 0195 #if HAVE_KHEALTHCERTIFICATE 0196 const auto lhsDt = KHealthCertificate::relevantUntil(lhs.cert); 0197 const auto rhsDt = KHealthCertificate::relevantUntil(rhs.cert); 0198 if (lhsDt == rhsDt) { 0199 return lhs.name < rhs.name; 0200 } 0201 if (!lhsDt.isValid()) { 0202 return false; 0203 } 0204 return !rhsDt.isValid() || lhsDt > rhsDt; 0205 #else 0206 return lhs.name < rhs.name; 0207 #endif 0208 } 0209 0210 #include "moc_healthcertificatemanager.cpp"