9#include "boardingpass.h"
20#include <QJsonDocument>
23#include <QRegularExpression>
24#include <QStringDecoder>
29using namespace KPkPass;
31static const char *
const passTypes[] = {
"boardingPass",
"coupon",
"eventTicket",
"generic",
"storeCard"};
32static const auto passTypesCount =
sizeof(passTypes) /
sizeof(passTypes[0]);
41 const auto it = messages.constFind(key);
42 if (it != messages.constEnd()) {
48void PassPrivate::parse()
52 for (
auto lang : langs) {
55 lang = lang.left(idx);
58 if (parseMessages(lang)) {
64 parseMessages(QStringLiteral(
"en.lproj"));
69 for (
int i =
start; i < catalog.
size(); ++i) {
70 const QChar catalogChar = catalog.
at(i);
71 if (catalogChar == c) {
86 for (
int i = 0; i < str.
size(); ++i) {
87 const auto c1 = str.
at(i);
89 const auto c2 = str.
at(i + 1);
108bool PassPrivate::parseMessages(
const QString &lang)
110 auto entry = zip->directory()->entry(lang);
111 if (!entry || !entry->isDirectory()) {
116 auto file =
dir->file(QStringLiteral(
"pass.strings"));
121 std::unique_ptr<QIODevice> dev(file->createDevice());
122 const auto rawData = dev->readAll();
123 if (rawData.size() < 4) {
130 if (std::ispunct((
unsigned char)rawData.at(0))) {
134 catalog = codec(rawData);
138 while (idx < catalog.
size()) {
140 const auto keyBegin = indexOfUnquoted(catalog,
QLatin1Char(
'"'), idx) + 1;
144 const auto keyEnd = indexOfUnquoted(catalog,
QLatin1Char(
'"'), keyBegin);
145 if (keyEnd <= keyBegin) {
150 const auto valueBegin = indexOfUnquoted(catalog,
QLatin1Char(
'"'), keyEnd + 2) + 1;
151 if (valueBegin <= keyEnd) {
154 const auto valueEnd = indexOfUnquoted(catalog,
QLatin1Char(
'"'), valueBegin);
155 if (valueEnd < valueBegin) {
159 const auto key = catalog.
mid(keyBegin, keyEnd - keyBegin);
160 const auto value = unquote(
QStringView(catalog).mid(valueBegin, valueEnd - valueBegin));
161 messages.insert(key, value);
170 const auto a = passData().
value(fieldType).toArray();
173 for (
const auto &v : a) {
179Pass *PassPrivate::fromData(std::unique_ptr<QIODevice> device,
QObject *parent)
181 std::unique_ptr<KZip> zip(
new KZip(device.get()));
187 auto file = zip->directory()->file(QStringLiteral(
"pass.json"));
191 std::unique_ptr<QIODevice> dev(file->createDevice());
193 const auto data = dev->readAll();
196 qCWarning(
Log) <<
"Error parsing pass.json:" <<
error.errorString() <<
error.offset;
200 s.replace(
QRegularExpression(QStringLiteral(R
"(\}[\s\n]*,[\s\n]*\})")), QStringLiteral("}}"));
201 s.replace(
QRegularExpression(QStringLiteral(R
"(\][\s\n]*,[\s\n]*\})")), QStringLiteral("]}"));
204 qCWarning(
Log) <<
"JSON syntax workarounds didn't help either:" <<
error.errorString() <<
error.offset;
209 qCWarning(
Log) <<
"pass.json has unsupported format version!";
214 int passTypeIdx = -1;
215 for (
unsigned int i = 0; i < passTypesCount; ++i) {
217 passTypeIdx =
static_cast<int>(i);
221 if (passTypeIdx < 0) {
222 qCWarning(
Log) <<
"pkpass file has no pass data structure!";
226 Pass *pass =
nullptr;
227 switch (passTypeIdx) {
228 case Pass::BoardingPass:
236 pass->d->buffer = std::move(device);
237 pass->d->zip = std::move(zip);
238 pass->d->passObj = passObj;
243Pass::Pass(Type passType,
QObject *parent)
247 d->passType = passType;
250Pass::~Pass() =
default;
257QString Pass::description()
const
259 return d->passObj.value(QLatin1StringView(
"description")).toString();
262QString Pass::organizationName()
const
264 return d->passObj.value(QLatin1StringView(
"organizationName")).toString();
267QString Pass::passTypeIdentifier()
const
269 return d->passObj.value(QLatin1StringView(
"passTypeIdentifier")).toString();
272QString Pass::serialNumber()
const
274 return d->passObj.value(QLatin1StringView(
"serialNumber")).toString();
277QDateTime Pass::expirationDate()
const
282bool Pass::isVoided()
const
284 return d->passObj.value(QLatin1StringView(
"voided")).toString() == QLatin1StringView(
"true");
292 for (
const auto &loc : a) {
315 return QColor(l[0].trimmed().toInt(), l[1].trimmed().toInt(), l[2].trimmed().toInt());
320QColor Pass::backgroundColor()
const
322 return parseColor(d->passObj.value(QLatin1StringView(
"backgroundColor")).
toString());
325QColor Pass::foregroundColor()
const
327 return parseColor(d->passObj.value(QLatin1StringView(
"foregroundColor")).
toString());
330QString Pass::groupingIdentifier()
const
332 return d->passObj.value(QLatin1StringView(
"groupingIdentifier")).toString();
335QColor Pass::labelColor()
const
337 const auto c = parseColor(d->passObj.value(QLatin1StringView(
"labelColor")).
toString());
341 return foregroundColor();
344QString Pass::logoText()
const
346 return d->message(d->passObj.value(QLatin1StringView(
"logoText")).
toString());
351 const auto entries = d->zip->directory()->entries();
352 for (
const auto &entry : entries) {
353 if (entry.startsWith(baseName)
362bool Pass::hasIcon()
const
364 return hasImage(QStringLiteral(
"icon"));
367bool Pass::hasLogo()
const
369 return hasImage(QStringLiteral(
"logo"));
372bool Pass::hasStrip()
const
374 return hasImage(QStringLiteral(
"strip"));
377bool Pass::hasBackground()
const
379 return hasImage(QStringLiteral(
"background"));
382bool Pass::hasFooter()
const
384 return hasImage(QStringLiteral(
"footer"));
387bool Pass::hasThumbnail()
const
389 return hasImage(QStringLiteral(
"thumbnail"));
397 auto dpr = devicePixelRatio;
398 for (; dpr > 0; --dpr) {
399 const auto it = d->m_images.find(ImageCacheKey{baseName, dpr});
400 if (it != d->m_images.end()) {
423 d->m_images[ImageCacheKey{baseName, dpr}] = img;
424 if (dpr != devicePixelRatio) {
425 d->m_images[ImageCacheKey{baseName, devicePixelRatio}] = img;
432 return image(QStringLiteral(
"icon"), devicePixelRatio);
437 return image(QStringLiteral(
"logo"), devicePixelRatio);
442 return image(QStringLiteral(
"strip"), devicePixelRatio);
447 return image(QStringLiteral(
"background"), devicePixelRatio);
452 return image(QStringLiteral(
"footer"), devicePixelRatio);
457 return image(QStringLiteral(
"thumbnail"), devicePixelRatio);
460QString Pass::authenticationToken()
const
465QUrl Pass::webServiceUrl()
const
472 QUrl url(webServiceUrl());
487 for (
const auto &bc : a)
500static const char *
const fieldNames[] = {
"auxiliaryFields",
"backFields",
"headerFields",
"primaryFields",
"secondaryFields"};
501static const auto fieldNameCount =
sizeof(fieldNames) /
sizeof(fieldNames[0]);
508QList<Field> Pass::backFields()
const
510 return d->fields(QLatin1StringView(fieldNames[1]),
this);
513QList<Field> Pass::headerFields()
const
515 return d->fields(QLatin1StringView(fieldNames[2]),
this);
518QList<Field> Pass::primaryFields()
const
520 return d->fields(QLatin1StringView(fieldNames[3]),
this);
523QList<Field> Pass::secondaryFields()
const
525 return d->fields(QLatin1StringView(fieldNames[4]),
this);
530 for (
unsigned int i = 0; i < fieldNameCount; ++i) {
532 for (
const auto &f : fs) {
533 if (f.key() == key) {
544 for (
unsigned int i = 0; i < fieldNameCount; ++i) {
552 std::unique_ptr<QBuffer> buffer(
new QBuffer);
553 buffer->setData(data);
555 return PassPrivate::fromData(std::move(buffer),
parent);
560 std::unique_ptr<QFile> file(
new QFile(fileName));
562 return PassPrivate::fromData(std::move(file),
parent);
564 qCWarning(
Log) <<
"Failed to open" << fileName <<
":" << file->errorString();
568QVariantMap Pass::fieldsVariantMap()
const
571 const auto elems =
fields();
572 std::for_each(elems.begin(), elems.end(), [&m](
const Field &f) {
573 m.insert(f.key(), QVariant::fromValue(f));
580 const auto prevPos = d->buffer->pos();
582 const auto data = d->buffer->readAll();
583 d->buffer->seek(prevPos);
587#include "moc_pass.cpp"
virtual QIODevice * createDevice() const
Field element in a KPkPass::Pass.
Base class for a pkpass file.
QImage image(const QString &baseName, unsigned int devicePixelRatio=1) const
Returns an image asset of this pass.
int maximumDistance() const
Distance in meters to any of the pass locations before this pass becomes relevant.
QUrl passUpdateUrl() const
Pass update URL.
Q_INVOKABLE QImage icon(unsigned int devicePixelRatio=1) const
Returns the pass icon.
Q_INVOKABLE QImage logo(unsigned int devicePixelRatio=1) const
Returns the pass logo.
bool hasImage(const QString &baseName) const
Returns true if an image asset with the given base name exists.
static Pass * fromData(const QByteArray &data, QObject *parent=nullptr)
Create a appropriate sub-class based on the pkpass file type.
static Pass * fromFile(const QString &fileName, QObject *parent=nullptr)
Create a appropriate sub-class based on the pkpass file type.
Q_INVOKABLE QImage thumbnail(unsigned int devicePixelRatio=1) const
Returns the thumbnail image if present.
QList< Field > fields() const
Returns all fields found in this pass.
Q_INVOKABLE QImage strip(unsigned int devicePixelRatio=1) const
Returns the strip image if present.
Q_INVOKABLE QImage background(unsigned int devicePixelRatio=1) const
Returns the background image if present.
QByteArray rawData() const
The raw data of this pass.
Q_INVOKABLE QImage footer(unsigned int devicePixelRatio=1) const
Returns the footer image if present.
Q_SCRIPTABLE QString start(QString train="")
char * toString(const EngineQuery &query)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
QImage fromData(QByteArrayView data, const char *format)
bool isNull() const const
void setDevicePixelRatio(qreal scaleFactor)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QJsonValue value(QLatin1StringView key) const const
QJsonObject toObject() const const
bool isEmpty() const const
void push_back(parameter_type value)
void reserve(qsizetype size)
T value(qsizetype i) const const
QStringList uiLanguages() const const
QObject * parent() const const
const QChar at(qsizetype position) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
void reserve(qsizetype size)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QStringView mid(qsizetype start, qsizetype length) const const
QChar at(qsizetype n) const const
qsizetype size() const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar ch) const const
bool isValid() const const
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)