KItinerary

online-ticket-dump.cpp
1/*
2 SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include <kitinerary_version.h>
7
8#include <KItinerary/ExtractorEngine>
9#include <KItinerary/ExtractorResult>
10#include <KItinerary/HttpResponse>
11
12#include <QCommandLineParser>
13#include <QCoreApplication>
14#include <QDateTime>
15#include <QFile>
16#include <QJsonDocument>
17#include <QJsonObject>
18#include <QNetworkAccessManager>
19#include <QNetworkReply>
20
21#include <iostream>
22
23using namespace KItinerary;
24
25static void harPostRequest(const QNetworkRequest &req, const QByteArray &postData, QJsonObject &harEntry)
26{
27 QJsonArray headers;
28 const auto rawHeaders = req.rawHeaderList();
29 for (const auto &h : rawHeaders) {
30 headers.push_back(QJsonObject({
33 }));
34 }
35
36 harEntry.insert(
37 QLatin1StringView("request"),
39 {QLatin1StringView("method"), QLatin1StringView("POST")},
40 {QLatin1StringView("url"), req.url().toString()},
41 {QLatin1StringView("headers"), headers},
42 {QLatin1StringView("postData"),
44 {QLatin1StringView("text"), QString::fromUtf8(postData)},
45 })},
46 }));
47}
48
49static void harResponse(const HttpResponse &response, QJsonObject &harEntry)
50{
51 harEntry.insert(
52 QLatin1StringView("response"),
54 {{QLatin1StringView("content"),
56 {QLatin1StringView("text"),
57 QString::fromUtf8(response.content().toBase64())},
58 {QLatin1StringView("encoding"), QLatin1StringView("base64")},
59 })}}));
60}
61
62int main(int argc, char **argv)
63{
64 QCoreApplication::setApplicationName(QStringLiteral("online-ticket-dump"));
65 QCoreApplication::setApplicationVersion(QStringLiteral(KITINERARY_VERSION_STRING));
66 QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
67 QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
68 QCoreApplication app(argc, argv);
69
70 QCommandLineParser parser;
71 parser.setApplicationDescription(QStringLiteral("Dump online ticket data."));
72 parser.addHelpOption();
73 parser.addVersionOption();
74 QCommandLineOption nameOpt(QStringLiteral("name"), QStringLiteral("Passenger last name."), QStringLiteral("name"));
75 parser.addOption(nameOpt);
76 QCommandLineOption refOpt(QStringLiteral("ref"), QStringLiteral("Ticket reference number."), QStringLiteral("ref"));
77 parser.addOption(refOpt);
78 QCommandLineOption kwidOpt(QStringLiteral("kwid"), QStringLiteral("DB kwid UUID."), QStringLiteral("ref"));
79 parser.addOption(kwidOpt);
80 QCommandLineOption sourceOpt(QStringLiteral("source"), QStringLiteral("Ticket provider (db or sncf)."), QStringLiteral("provider"));
81 parser.addOption(sourceOpt);
82 QCommandLineOption harOpt(QStringLiteral("har"), QStringLiteral("File to write HTTP communication to."), QStringLiteral("file"));
83 parser.addOption(harOpt);
84 parser.process(app);
85
86 if (!parser.isSet(nameOpt) || !parser.isSet(refOpt) || !parser.isSet(sourceOpt)) {
87 parser.showHelp(1);
88 }
89
92 QNetworkReply *reply = nullptr;
93
94 QJsonObject harEntry{
95 {QLatin1StringView("startDateTime"),
97 if (parser.value(sourceOpt) == QLatin1StringView("db")) {
99 QUrl(QStringLiteral("https://fahrkarten.bahn.de/mobile/dbc/xs.go?")));
101 QByteArray("application/x-www-form-urlencoded"));
102 QByteArray postData(
103 "<rqorderdetails version=\"1.0\"><rqheader v=\"23080000\" os=\"KCI\" "
104 "app=\"KCI-Webservice\"/><rqorder on=\"" +
105 parser.value(refOpt).toUtf8() +
106 (parser.isSet(kwidOpt)
107 ? QByteArray("\" kwid=\"" + parser.value(kwidOpt).toUtf8())
108 : QByteArray()) +
109 "\"/><authname tln=\"" + parser.value(nameOpt).toUtf8() +
110 "\"/></rqorderdetails>");
111 reply = nam.post(req, postData);
112 harPostRequest(req, postData, harEntry);
113 } else if (parser.value(sourceOpt) == QLatin1StringView("sncf")) {
114 // based on https://www.sncf-connect.com/app/trips/search and stripped to
115 // the bare minimum that works
116 QNetworkRequest req(QUrl(QStringLiteral(
117 "https://www.sncf-connect.com/bff/api/v1/trips/trips-by-criteria")));
119 QByteArray("application/json"));
121 QByteArray("Mozilla/5.0 (X11; Linux x86_64; rv:109.0) "
122 "Gecko/20100101 Firefox/111.0"));
123 req.setRawHeader("Accept", "application/json, text/plain, */*");
124 req.setRawHeader("x-bff-key", "ah1MPO-izehIHD-QZZ9y88n-kku876");
125 QByteArray postData("{\"reference\":\"" + parser.value(refOpt).toUtf8() +
126 "\",\"name\":\"" + parser.value(nameOpt).toUtf8() +
127 "\"}");
128 reply = nam.post(req, postData);
129 harPostRequest(req, postData, harEntry);
130 }
131
132 if (!reply) {
133 parser.showHelp(1);
134 }
135
136 QObject::connect(reply, &QNetworkReply::finished, &app, [&app, &parser, &harOpt, &harEntry, reply]() {
137 reply->deleteLater();
138 const auto response = HttpResponse::fromNetworkReply(reply);
139 ExtractorEngine engine;
140 engine.setContent(response, u"internal/http-response");
141 qDebug().noquote() << QJsonDocument(engine.extract()).toJson();
142
143 if (const auto harPath = parser.value(harOpt); !harPath.isEmpty()) {
144 QFile f(harPath);
145 if (!f.open(QFile::WriteOnly)) {
146 qWarning() << f.errorString();
147 }
148
149 harResponse(response, harEntry);
150 QJsonObject har({
151 {QLatin1StringView("log"),
153 {{QLatin1StringView("entries"), QJsonArray({harEntry})}})},
154 });
155 f.write(QJsonDocument(har).toJson());
156 }
157
158 app.quit();
159 });
160
161 return app.exec();
162}
Semantic data extraction engine.
void setContent(const QVariant &data, QStringView mimeType)
Already decoded data to extract from.
QJsonArray extract()
Perform the actual extraction, and return the JSON-LD data that has been found.
Container for an HTTP response to be passed into the extractor engine.
static HttpResponse fromNetworkReply(QNetworkReply *reply)
Create a HttpResponse object from an active QNetworkReply.
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
QByteArray toBase64(Base64Options options) const const
QCommandLineOption addHelpOption()
bool addOption(const QCommandLineOption &option)
QCommandLineOption addVersionOption()
bool isSet(const QCommandLineOption &option) const const
void process(const QCoreApplication &app)
void setApplicationDescription(const QString &description)
void showHelp(int exitCode)
QString value(const QCommandLineOption &option) const const
void setApplicationName(const QString &application)
void setApplicationVersion(const QString &version)
void setOrganizationDomain(const QString &orgDomain)
void setOrganizationName(const QString &orgName)
QDateTime currentDateTime()
QString toString(QStringView format, QCalendar cal) const const
void push_back(const QJsonValue &value)
QByteArray toJson(JsonFormat format) const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
QNetworkReply * post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
QByteArray rawHeader(const QByteArray &headerName) const const
QList< QByteArray > rawHeaderList() const const
void setHeader(KnownHeaders header, const QVariant &value)
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
QUrl url() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QByteArray toUtf8() const const
ISODateWithMs
QString toString(FormattingOptions options) const const
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.