uic9183parser.cpp 6.58 KB
Newer Older
1
/*
2
    SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
3

4
    SPDX-License-Identifier: LGPL-2.0-or-later
5
6
7
8
*/

#include "uic9183parser.h"
#include "logging.h"
9
#include "rct2ticket.h"
10
#include "uic9183block.h"
11
#include "uic9183head.h"
Volker Krause's avatar
Volker Krause committed
12
#include "uic9183header.h"
13
#include "uic9183ticketlayout.h"
14
#include "vendor0080block.h"
15

16
#include <QDateTime>
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <QDebug>

#include <zlib.h>

#include <cassert>
#include <cstring>

using namespace KItinerary;

namespace KItinerary {

class Uic9183ParserPrivate : public QSharedData
{
public:
Volker Krause's avatar
Volker Krause committed
31
    QByteArray m_data;
32
33
34
35
    QByteArray m_payload;
};
}

36
37
38
39
40
41
42
43
Uic9183Parser::Uic9183Parser()
    : d(new Uic9183ParserPrivate)
{
}

Uic9183Parser::Uic9183Parser(const Uic9183Parser&) = default;
Uic9183Parser::~Uic9183Parser() = default;
Uic9183Parser& Uic9183Parser::operator=(const Uic9183Parser&) = default;
44

Volker Krause's avatar
Volker Krause committed
45
Uic9183Block Uic9183Parser::firstBlock() const
46
{
Volker Krause's avatar
Volker Krause committed
47
48
49
50
51
    return Uic9183Block(d->m_payload, 0);
}

Uic9183Block Uic9183Parser::findBlock(const char name[6]) const
{
52
53
    for (auto block = firstBlock(); !block.isNull(); block = block.nextBlock()) {
        if (block.isA(name)) {
54
            return block;
55
56
57
58
59
        }
    }
    return {};
}

60
QVariant Uic9183Parser::block(const QString &name) const
61
{
62
63
64
    if (name.size() != 6 || d->m_payload.isEmpty()) {
        return {};
    }
65

66
67
    return QVariant::fromValue(findBlock(name.toUtf8().constData()));
}
68

69
void Uic9183Parser::setContextDate(const QDateTime&)
70
71
72
{
}

73
74
void Uic9183Parser::parse(const QByteArray &data)
{
Volker Krause's avatar
Volker Krause committed
75
    d->m_data.clear();
76
77
    d->m_payload.clear();

Volker Krause's avatar
Volker Krause committed
78
79
    Uic9183Header header(data);
    if (!header.isValid()) {
80
81
82
83
        return;
    }

    // nx zlib payload
Volker Krause's avatar
Volker Krause committed
84
    d->m_data = data;
85
86
87
88
89
    d->m_payload.resize(4096);
    z_stream stream;
    stream.zalloc = nullptr;
    stream.zfree = nullptr;
    stream.opaque = nullptr;
Volker Krause's avatar
Volker Krause committed
90
91
    stream.avail_in = data.size() - header.compressedMessageOffset();
    stream.next_in = reinterpret_cast<unsigned char*>(const_cast<char*>(data.data() + header.compressedMessageOffset()));
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    stream.avail_out = d->m_payload.size();
    stream.next_out = reinterpret_cast<unsigned char*>(d->m_payload.data());

    inflateInit(&stream);
    const auto res = inflate(&stream, Z_NO_FLUSH);
    switch (res) {
        case Z_OK:
        case Z_STREAM_END:
            break; // all good
        default:
            qCWarning(Log) << "UIC 918.3 payload zlib decompression failed" << stream.msg;
            return;
    }
    inflateEnd(&stream);
    d->m_payload.truncate(d->m_payload.size() - stream.avail_out);
Volker Krause's avatar
Volker Krause committed
107
    //qCDebug(Log) << res <<  d->m_payload << stream.avail_out;
108
109
110
111
112
113
114
115
116
}

bool Uic9183Parser::isValid() const
{
    return !d->m_payload.isEmpty();
}

QString Uic9183Parser::pnr() const
{
117
    return findBlock<Uic9183Head>().ticketKey().left(6);
118
119
}

120
121
QString Uic9183Parser::carrierId() const
{
122
    return findBlock<Uic9183Head>().issuerCompanyCodeString();
123
124
}

125
126
Person Uic9183Parser::person() const
{
127
    // Deutsche Bahn vendor block
128
    const auto b = findBlock<Vendor0080BLBlock>();
129
130
131
132
    if (b.isValid()) {
        // S028 contains family and given name separated by a '#', UTF-8 encoded
        auto sblock = b.findSubBlock("028");
        if (!sblock.isNull()) {
133
134
            const auto endIt = sblock.content() + sblock.contentSize();
            auto it = std::find(sblock.content(), endIt, '#');
135
136
            if (it != endIt) {
                Person p;
137
                p.setGivenName(QString::fromUtf8(sblock.content(), std::distance(sblock.content(), it)));
138
139
140
141
142
143
144
145
146
                ++it;
                p.setFamilyName(QString::fromUtf8(it, std::distance(it, endIt)));
                return p;
            }
        }
        // S023 contains the full name, UTF-8 encoded
        sblock = b.findSubBlock("023");
        if (!sblock.isNull()) {
            Person p;
147
            p.setName(sblock.toString());
148
            return p;
149
150
        }
    }
151
152
153
154

    // RCT2 tickets
    const auto rct2 = rct2Ticket();
    if (rct2.isValid()) {
155
        const auto name = rct2.passengerName();
156
157
158
159
160
161
162
        if (!name.isEmpty()) {
            Person p;
            p.setName(name);
            return p;
        }
    }

163
164
165
    return {};
}

166
167
QString Uic9183Parser::outboundDepartureStationId() const
{
168
    const auto b = findBlock<Vendor0080BLBlock>();
169
170
171
    if (b.isValid()) {
        // S035 contains the IBNR, possible with leading '80' country code and leading 0 stripped
        const auto sblock = b.findSubBlock("035");
172
        if (!sblock.isNull() && sblock.contentSize() <= 7) {
173
174
175
176
177
178
179
180
181
182
            QString ibnr = QStringLiteral("ibnr:8000000");
            const auto s = sblock.toString();
            return ibnr.replace(ibnr.size() - s.size(), s.size(), s);
        }
    }
    return {};
}

QString Uic9183Parser::outboundArrivalStationId() const
{
183
    const auto b = findBlock<Vendor0080BLBlock>();
184
185
186
    if (b.isValid()) {
        // S036 contains the IBNR, possible with leading '80' country code and leading 0 stripped
        const auto sblock = b.findSubBlock("036");
187
        if (!sblock.isNull() && sblock.contentSize() <= 7) {
188
189
190
191
192
193
194
195
            QString ibnr = QStringLiteral("ibnr:8000000");
            const auto s = sblock.toString();
            return ibnr.replace(ibnr.size() - s.size(), s.size(), s);
        }
    }
    return {};
}

196
197
QString Uic9183Parser::seatingType() const
{
198
    const auto b = findBlock<Vendor0080BLBlock>();;
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    if (b.isValid()) {
        // S014 contains the class, possibly with a leading 'S' for some reason
        const auto sblock = b.findSubBlock("014");
        if (!sblock.isNull()) {
            const auto s = sblock.toString();
            return s.startsWith(QLatin1Char('S')) ? s.right(1) : s;
        }
    }

    const auto rct2 = rct2Ticket();
    if (rct2.isValid()) {
        return rct2.outboundClass();
    }
    return {};
}

215
Uic9183TicketLayout Uic9183Parser::ticketLayout() const
216
{
217
    return findBlock<Uic9183TicketLayout>();
218
219
220
221
222
223
224
225
226
227
228
}

QVariant Uic9183Parser::ticketLayoutVariant() const
{
    const auto layout = ticketLayout();
    return layout.isValid() ? QVariant::fromValue(layout) : QVariant();
}

Rct2Ticket Uic9183Parser::rct2Ticket() const
{
    Rct2Ticket rct2(ticketLayout());
229
230
    const auto u_head = findBlock<Uic9183Head>();
    rct2.setContextDate(u_head.issuingDateTime());
231
    return rct2;
232
233
234
235
236
237
238
239
240
241
242
}

QVariant Uic9183Parser::rct2TicketVariant() const
{
    const auto rct2 = rct2Ticket();
    if (rct2.isValid()) {
        return QVariant::fromValue(rct2);
    }
    return {};
}

Volker Krause's avatar
Volker Krause committed
243
Uic9183Header Uic9183Parser::header() const
244
{
Volker Krause's avatar
Volker Krause committed
245
246
    return Uic9183Header(d->m_data);
}
247

248
249
250
251
252
QByteArray Uic9183Parser::rawData() const
{
    return d->m_data;
}

Volker Krause's avatar
Volker Krause committed
253
254
255
256
bool Uic9183Parser::maybeUic9183(const QByteArray& data)
{
    Uic9183Header h(data);
    return h.isValid();
257
}
258

259
#include "moc_uic9183parser.cpp"