healthcertificatemanager.cpp 6.02 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
    SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "config-itinerary.h"
#include "healthcertificatemanager.h"

#if HAVE_KHEALTHCERTIFICATE
#include <KHealthCertificate/KHealthCertificateParser>
#include <KHealthCertificate/KRecoveryCertificate>
#include <KHealthCertificate/KTestCertificate>
#include <KHealthCertificate/KVaccinationCertificate>
#endif

#include <KLocalizedString>

#include <QClipboard>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QGuiApplication>
#include <QMimeData>
#include <QStandardPaths>
#include <QUuid>

HealthCertificateManager::HealthCertificateManager(QObject *parent)
    : QAbstractListModel(parent)
{
    loadCertificates();
}

HealthCertificateManager::~HealthCertificateManager() = default;

bool HealthCertificateManager::isAvailable() const
{
#if HAVE_KHEALTHCERTIFICATE
    return true;
#else
    return false;
#endif
}

static QString basePath()
{
    return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/health-certificates/");
}

void HealthCertificateManager::importCertificate(const QByteArray &rawData)
{
53
54
55
56
57
58
    // check whether we know this certificate already
    for (const auto &c : m_certificates) {
        if (certificateRawData(c) == rawData) {
            return;
        }
    }
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#if HAVE_KHEALTHCERTIFICATE
    CertData certData;
    certData.cert = KHealthCertificateParser::parse(rawData);
    if (certData.cert.isNull()) {
        return;
    }

    auto path = basePath();
    QDir().mkpath(path);
    certData.name = QUuid::createUuid().toString();
    path += QLatin1Char('/') + certData.name;

    QFile f(path);
    if (!f.open(QFile::WriteOnly)) {
        qWarning() << f.errorString() << f.fileName();
        return;
    }
    f.write(rawData);
    f.close();

79
80
81
82
    const auto it = std::lower_bound(m_certificates.begin(), m_certificates.end(), certData, certLessThan);
    const auto row = std::distance(m_certificates.begin(), it);
    beginInsertRows({}, row, row);
    m_certificates.insert(it, std::move(certData));
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
    endInsertRows();
#endif
}

void HealthCertificateManager::importCertificateFromClipboard()
{
    const auto md = QGuiApplication::clipboard()->mimeData();
    if (md->hasText()) {
        importCertificate(md->text().toUtf8());
    } else if (md->hasFormat(QLatin1String("application/octet-stream"))) {
        importCertificate(md->data(QLatin1String("application/octet-stream")));
    }
}

void HealthCertificateManager::removeCertificate(int row)
{
    beginRemoveRows({}, row, row);
    const auto it = m_certificates.begin() + row;
    QFile::remove(basePath() + QLatin1Char('/') + (*it).name);
    m_certificates.erase(it);
    endRemoveRows();
}

int HealthCertificateManager::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid()) {
        return 0;
    }
    return m_certificates.size();
}

QVariant HealthCertificateManager::data(const QModelIndex &index, int role) const
{
116
    const auto &v = m_certificates[index.row()];
117
118
119
    switch (role) {
        case Qt::DisplayRole:
#if HAVE_KHEALTHCERTIFICATE
120
121
            if (v.cert.userType() == qMetaTypeId<KVaccinationCertificate>()) {
                const auto cert = v.cert.value<KVaccinationCertificate>();
122
123
                return i18n("Vaccination %1/%2 (%3)", cert.dose(), cert.totalDoses(), cert.name());
            }
124
125
            if (v.cert.userType() == qMetaTypeId<KTestCertificate>()) {
                const auto cert = v.cert.value<KTestCertificate>();
126
127
                return i18n("Test %1 (%2)", QLocale().toString(cert.date(), QLocale::NarrowFormat), cert.name());
            }
128
129
            if (v.cert.userType() == qMetaTypeId<KRecoveryCertificate>()) {
                const auto cert = v.cert.value<KRecoveryCertificate>();
130
131
132
133
134
                return i18n("Recovery (%1)", cert.name());
            }
#endif
            return {};
        case CertificateRole:
135
136
            return v.cert;
        case RawDataRole:
137
            return certificateRawData(v);
138
139
        case StorageIdRole:
            return v.name;
140
141
142
143
144
145
146
147
    }
    return {};
}

QHash<int, QByteArray> HealthCertificateManager::roleNames() const
{
    auto rns = QAbstractListModel::roleNames();
    rns.insert(CertificateRole, "certificate");
148
149
    rns.insert(RawDataRole, "rawData");
    rns.insert(StorageIdRole, "storageId");
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
    return rns;
}

void HealthCertificateManager::loadCertificates()
{
    beginResetModel();
    for (QDirIterator it(basePath(), QDir::Files); it.hasNext();) {
        QFile f(it.next());
        if (!f.open(QFile::ReadOnly)) {
            qWarning() << f.errorString() << f.fileName();
            continue;
        }

        const auto rawData = f.readAll();
        CertData certData;
        certData.name = it.fileName();
#if HAVE_KHEALTHCERTIFICATE
        certData.cert = KHealthCertificateParser::parse(rawData);
#endif
        if (certData.cert.isNull()) {
            continue;
        }

        m_certificates.push_back(std::move(certData));
    }
175
    std::sort(m_certificates.begin(), m_certificates.end(), certLessThan);
176
177
    endResetModel();
}
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

QByteArray HealthCertificateManager::certificateRawData(const CertData &certData) const
{
#if HAVE_KHEALTHCERTIFICATE
    if (certData.cert.userType() == qMetaTypeId<KVaccinationCertificate>()) {
        return certData.cert.value<KVaccinationCertificate>().rawData();
    }
    if (certData.cert.userType() == qMetaTypeId<KTestCertificate>()) {
        return certData.cert.value<KTestCertificate>().rawData();
    }
    if (certData.cert.userType() == qMetaTypeId<KRecoveryCertificate>()) {
        return certData.cert.value<KRecoveryCertificate>().rawData();
    }
#endif
    return {};
}
194
195
196

bool HealthCertificateManager::certLessThan(const CertData &lhs, const CertData &rhs)
{
197
#if HAVE_KHEALTHCERTIFICATE
198
199
200
201
202
203
    const auto lhsDt = KHealthCertificate::relevantUntil(lhs.cert);
    const auto rhsDt = KHealthCertificate::relevantUntil(rhs.cert);
    if (lhsDt == rhsDt) {
        return lhs.name < rhs.name;
    }
    return lhsDt > rhsDt;
204
205
206
#else
    return lhs.name < rhs.name;
#endif
207
}