File indexing completed on 2025-02-02 05:02:32
0001 /* 0002 SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #include "onlineticketretrievaljob.h" 0007 0008 #include <KItinerary/ExtractorEngine> 0009 #include <KItinerary/ExtractorResult> 0010 #include <KItinerary/HttpResponse> 0011 #include <KItinerary/JsonLdDocument> 0012 0013 #include <QNetworkAccessManager> 0014 #include <QNetworkReply> 0015 #include <QXmlStreamReader> 0016 0017 OnlineTicketRetrievalJob::OnlineTicketRetrievalJob(const QString &sourceId, const QVariantMap &arguments, QNetworkAccessManager *nam, QObject *parent) 0018 : QObject(parent) 0019 , m_nam(nam) 0020 { 0021 QNetworkReply *reply = nullptr; 0022 0023 // TODO this should be done more modular eventually, similar to the extraction side 0024 // e.g. by using some form of request templates, or by service-specific scripts 0025 if (sourceId == QLatin1StringView("db")) { 0026 const auto ref = arguments.value(QLatin1StringView("reference")).toString(); 0027 if (ref.size() == 6) { 0028 dbRequestOrderDetails(arguments); 0029 } else { 0030 dbRequestFindOrder(arguments); 0031 } 0032 return; 0033 } 0034 if (sourceId == QLatin1StringView("sncf")) { 0035 // based on https://www.sncf-connect.com/app/trips/search and stripped to the bare minimum that works 0036 QNetworkRequest req(QUrl(QStringLiteral("https://www.sncf-connect.com/bff/api/v1/trips/trips-by-criteria"))); 0037 req.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("application/json")); 0038 req.setHeader(QNetworkRequest::UserAgentHeader, QByteArray("Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0")); 0039 req.setRawHeader("Accept", "application/json, text/plain, */*"); 0040 req.setRawHeader("x-bff-key", "ah1MPO-izehIHD-QZZ9y88n-kku876"); 0041 QByteArray postData("{\"reference\":\"" + arguments.value(QLatin1StringView("reference")).toString().toUtf8() + "\",\"name\":\"" + arguments.value(QLatin1StringView("name")).toString().toUtf8() + "\"}"); 0042 reply = nam->post(req, postData); 0043 reply->setParent(this); 0044 connect(reply, &QNetworkReply::finished, this, [reply, this]() { handleReply(reply); }); 0045 return; 0046 } 0047 0048 QMetaObject::invokeMethod(this, &OnlineTicketRetrievalJob::finished, Qt::QueuedConnection); 0049 } 0050 0051 OnlineTicketRetrievalJob::~OnlineTicketRetrievalJob() = default; 0052 0053 QVector<QVariant> OnlineTicketRetrievalJob::result() const 0054 { 0055 return m_result; 0056 } 0057 0058 QString OnlineTicketRetrievalJob::errorMessage() const 0059 { 0060 return m_errorMsg; 0061 } 0062 0063 void OnlineTicketRetrievalJob::dbRequestFindOrder(const QVariantMap &arguments) 0064 { 0065 QNetworkRequest req(QUrl(QStringLiteral("https://fahrkarten.bahn.de/mobile/dbc/xs.go?"))); 0066 req.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("application/x-www-form-urlencoded")); 0067 QByteArray postData(R"(<rqfindorder version="1.0"><rqheader v="23080000" os="KCI" app="NAVIGATOR"/><rqorder on=")" 0068 + arguments.value(QLatin1StringView("reference")).toString().toUtf8() 0069 + R"("/><authname tln=")" 0070 + arguments.value(QLatin1StringView("name")).toString().toUtf8() 0071 + R"("/></rqfindorder>)"); 0072 auto reply = m_nam->post(req, postData); 0073 reply->setParent(this); 0074 connect(reply, &QNetworkReply::finished, this, [reply, arguments, this]() { 0075 reply->deleteLater(); 0076 auto args = arguments; 0077 args.insert(QLatin1StringView("kwid"), dbParseKwid(reply)); 0078 dbRequestOrderDetails(args); 0079 }); 0080 } 0081 0082 QString OnlineTicketRetrievalJob::dbParseKwid(QIODevice *io) 0083 { 0084 QXmlStreamReader reader(io); 0085 while (!reader.atEnd()) { 0086 reader.readNextStartElement(); 0087 if (const auto kwid = reader.attributes().value(QLatin1StringView("kwid")); !kwid.isEmpty()) { 0088 return kwid.toString(); 0089 } 0090 } 0091 return {}; 0092 } 0093 0094 void OnlineTicketRetrievalJob::dbRequestOrderDetails(const QVariantMap &arguments) 0095 { 0096 const auto kwid = arguments.value(QLatin1StringView("kwid")).toString(); 0097 0098 QNetworkRequest req(QUrl(QStringLiteral("https://fahrkarten.bahn.de/mobile/dbc/xs.go?"))); 0099 req.setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("application/x-www-form-urlencoded")); 0100 QByteArray postData(R"(<rqorderdetails version="1.0"><rqheader v="23040000" os="KCI" app="KCI-Webservice"/><rqorder on=")" 0101 + arguments.value(QLatin1StringView("reference")).toString().toUpper().toUtf8() 0102 + (kwid.isEmpty() ? QByteArray() : QByteArray(R"(" kwid=")" + kwid.toUtf8())) 0103 + R"("/><authname tln=")" 0104 + arguments.value(QLatin1StringView("name")).toString().toUtf8() 0105 + R"("/></rqorderdetails>)"); 0106 auto reply = m_nam->post(req, postData); 0107 reply->setParent(this); 0108 connect(reply, &QNetworkReply::finished, this, [reply, this]() { handleReply(reply); }); 0109 } 0110 0111 void OnlineTicketRetrievalJob::handleReply(QNetworkReply *reply) 0112 { 0113 reply->deleteLater(); 0114 if (reply->error() != QNetworkReply::NoError) { 0115 m_errorMsg = reply->errorString(); 0116 } else { 0117 using namespace KItinerary; 0118 ExtractorEngine engine; 0119 engine.setContent(HttpResponse::fromNetworkReply(reply), u"internal/http-response"); 0120 m_result = JsonLdDocument::fromJson(engine.extract()); 0121 } 0122 Q_EMIT finished(); 0123 } 0124 0125 #include "moc_onlineticketretrievaljob.cpp"