KItinerary

vdvticket.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "vdvticket.h"
8#include "vdvticketcontent.h"
9#include "vdvdata_p.h"
10#include "logging.h"
11
12#include "../asn1/berelement.h"
13
14#include <QDebug>
15#include <QStringDecoder>
16
17using namespace KItinerary;
18
19namespace KItinerary {
20class VdvTicketPrivate : public QSharedData
21{
22public:
23 QByteArray m_data;
24 QByteArray m_rawData;
25
26 BER::Element productElement(uint32_t type) const;
27 template <typename T> const T* productData(uint32_t type = T::Tag) const;
28};
29}
30
31BER::Element VdvTicketPrivate::productElement(uint32_t type) const
32{
33 const auto productElement = BER::TypedElement<TagTicketProductData>(m_data, sizeof(VdvTicketHeader));
34 if (!productElement.isValid()) {
35 return {};
36 }
37 return productElement.find(type);
38}
39
40template <typename T>
41const T* VdvTicketPrivate::productData(uint32_t type) const
42{
43 const auto elem = productElement(type);
44 if (elem.isValid()) {
45 return elem.template contentAt<T>();
46 }
47 return nullptr;
48}
49
50VdvTicket::VdvTicket()
51 : d(new VdvTicketPrivate)
52{
53}
54
55VdvTicket::VdvTicket(const QByteArray &data, const QByteArray &rawData)
56 : d(new VdvTicketPrivate)
57{
58 if (data.size() < MinimumTicketDataSize) {
59 qCWarning(Log) << "Ticket data too small" << data.size();
60 return;
61 }
62
63 static_assert(sizeof(VdvTicketHeader) < MinimumTicketDataSize, "");
64 int offset = sizeof(VdvTicketHeader);
65
66 const auto productBlock = BER::TypedElement<TagTicketProductData>(data, offset);
67 if (!productBlock.isValid() || productBlock.size() + offset > data.size()) {
68 qCWarning(Log) << "Invalid product block" << productBlock.isValid() << productBlock.size() << offset << data.size();
69 return;
70 }
71 offset += productBlock.size();
72 offset += sizeof(VdvTicketCommonTransactionData);
73
74 const auto prodTransactionBlock = BER::TypedElement<TagTicketProductTransactionData>(data, offset);
75 if (!prodTransactionBlock.isValid()) {
76 qCWarning(Log) << "Invalid product transaction block" << prodTransactionBlock.isValid() << offset << data.size();
77 return;
78 }
79 offset += prodTransactionBlock.size();
80 offset += sizeof(VdvTicketIssueData);
81
82 // 0 padding to reach the minimum size
83 // 111 in the specs known to us, but seems to vary in practice, so be flexible and
84 // just align with the input end
85 if (data.size() < offset + (int)sizeof(VdvTicketTrailer)) {
86 qCWarning(Log) << "Ticket data too small for VDV ticket trailer" << offset;
87 return;
88 }
89 offset = data.size() - (int)sizeof(VdvTicketTrailer);
90
91 const auto trailer = reinterpret_cast<const VdvTicketTrailer*>(data.constData() + offset);
92 if (memcmp(trailer->identifier, "VDV", 3) != 0) {
93 qCWarning(Log) << "Invalid ticket trailer identifier:" << QByteArray(trailer->identifier, 3) << trailer->version;
94 return;
95 }
96 d->m_data = data;
97 d->m_rawData = rawData;
98}
99
100VdvTicket::VdvTicket(const VdvTicket&) = default;
101VdvTicket::~VdvTicket() = default;
102VdvTicket& VdvTicket::operator=(const VdvTicket&) = default;
103
105{
106 const auto hdr = header();
107 return hdr ? hdr->validityBegin : QDateTime();
108}
109
111{
112 const auto hdr = header();
113 return hdr ? hdr->validityEnd : QDateTime();
114}
115
116int VdvTicket::issuerId() const
117{
118 const auto hdr = header();
119 return hdr ? hdr->kvpOrgId : 0;
120}
121
122int VdvTicket::operatorId() const
123{
124 const auto hdr = header();
125 return hdr ? hdr->pvOrgId : 0;
126}
127
128VdvTicket::ServiceClass VdvTicket::serviceClass() const
129{
130 const auto tlv = d->productData<VdvTicketBasicData>();
131 if (!tlv) {
132 return UnknownClass;
133 }
134 switch (tlv->serviceClass) {
135 case 0:
136 return UnknownClass;
137 case 1:
138 return FirstClass;
139 case 2:
140 return SecondClass;
141 case 3:
142 return FirstClassUpgrade;
143 }
144 qCDebug(Log) << "Unknown service class:" << tlv->serviceClass;
145 return UnknownClass;
146}
147
149{
150 const auto elem = d->productElement(VdvTicketTravelerData::Tag);
151 const auto tlv = elem.isValid() ? elem.contentAt<VdvTicketTravelerData>() : nullptr;
152 if (!tlv) {
153 return {};
154 }
155
156 const auto len = (qsizetype)strnlen(tlv->name(), tlv->nameSize(elem.contentSize())); // name field can contain null bytes
157 if (len == 0) {
158 return {};
159 }
160
161 QString name;
163 name = utf8Decoder.decode(QByteArrayView(tlv->name(), len));
164 if (utf8Decoder.hasError()) {
165 name = QString::fromLatin1(tlv->name(), len);
166 }
167
168 Person p;
169 const auto idxHash = name.indexOf(QLatin1Char('#'));
170 const auto idxAt = name.indexOf(QLatin1Char('@'));
171
172 // encoding as first#last
173 if (idxHash > 0) {
174 p.setFamilyName(name.mid(idxHash + 1));
175 p.setGivenName(name.left(idxHash));
176 }
177
178 // encoding as f1<len>fn@l1<len>ln
179 else if (idxAt > 0) {
180 p.setFamilyName(name.at(idxAt + 1));
181 p.setGivenName(name.at(0));
182 }
183
184 // unknown encoding
185 else {
186 p.setName(name);
187 }
188
189 return p;
190}
191
193{
194 const auto hdr = header();
195 return hdr ? QString::number(hdr->ticketId) : QString();
196}
197
198const VdvTicketHeader* VdvTicket::header() const
199{
200 return d->m_data.isEmpty() ? nullptr : reinterpret_cast<const VdvTicketHeader*>(d->m_data.constData());
201}
202
203BER::Element VdvTicket::productData() const
204{
205 const auto productElement = BER::Element(d->m_data, sizeof(VdvTicketHeader));
206 return (productElement.isValid() && productElement.type() == TagTicketProductData) ? productElement : BER::Element();
207}
208
209const VdvTicketCommonTransactionData* VdvTicket::commonTransactionData() const
210{
211 return d->m_data.isEmpty() ? nullptr :
212 reinterpret_cast<const VdvTicketCommonTransactionData*>(d->m_data.constData() + sizeof(VdvTicketHeader) + productData().size());
213}
214
215BER::Element VdvTicket::productSpecificTransactionData() const
216{
217 const auto offset = sizeof(VdvTicketHeader) + productData().size() + sizeof(VdvTicketCommonTransactionData);
218 const auto elem = BER::Element(d->m_data, offset);
219 return (elem.isValid() && elem.type() == TagTicketProductTransactionData) ? elem : BER::Element();
220}
221
222const VdvTicketIssueData* VdvTicket::issueData() const
223{
224 const auto offset = sizeof(VdvTicketHeader) + productData().size()
225 + sizeof(VdvTicketCommonTransactionData) + productSpecificTransactionData().size();
226 return d->m_data.isEmpty() ? nullptr : reinterpret_cast<const VdvTicketIssueData*>(d->m_data.constData() + offset);
227}
228
229const VdvTicketTrailer* VdvTicket::trailer() const
230{
231 // see above
232 const auto offset = d->m_data.size() - (int)sizeof(VdvTicketTrailer);
233 return d->m_data.isEmpty() ? nullptr : reinterpret_cast<const VdvTicketTrailer*>(d->m_data.constData() + offset);
234}
235
236QByteArray VdvTicket::rawData() const
237{
238 return d->m_rawData;
239}
240
241#include "moc_vdvticket.cpp"
An element in BER/DER/X.690 encoding.
Definition berelement.h:28
int size() const
Size of the entire element (type, size and content).
bool isValid() const
Returns true if this element has a valid structure and can be read from.
Element find(uint32_t type) const
Returns the first child element of the given type.
A person.
Definition person.h:20
Product specific data - basic information.
Ticket issuer data block.
Ticket trailer, after padding.
Product specific data - traveler information.
Ticket information from a VDV barcode.
Definition vdvticket.h:30
ServiceClass serviceClass
Service class for this ticket.
Definition vdvticket.h:42
QString ticketNumber
Ticket number.
Definition vdvticket.h:46
QDateTime endDateTime
End of the validity of this ticket.
Definition vdvticket.h:35
QDateTime beginDateTime
Begin of the validitiy of this ticket.
Definition vdvticket.h:33
int operatorId
VDV organization identifier of the operator.
Definition vdvticket.h:40
KItinerary::Person person
The person this ticket is valid for.
Definition vdvticket.h:44
int issuerId
VDV organization identifier of the ticket issuer.
Definition vdvticket.h:38
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
const char * constData() const const
qsizetype size() const const
QString fromLatin1(QByteArrayView str)
QString number(double n, char format, int precision)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:14:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.