File indexing completed on 2024-05-12 04:42:32
0001 /* 0002 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "abstractbackend.h" 0008 #include "logging.h" 0009 0010 #include <KPublicTransport/JourneyReply> 0011 #include <KPublicTransport/JourneyRequest> 0012 #include <KPublicTransport/Location> 0013 0014 #include <QDebug> 0015 #include <QDir> 0016 #include <QJsonDocument> 0017 #include <QNetworkReply> 0018 0019 using namespace KPublicTransport; 0020 0021 AbstractBackend::AbstractBackend() = default; 0022 AbstractBackend::~AbstractBackend() = default; 0023 0024 QString AbstractBackend::backendId() const 0025 { 0026 return m_backendId; 0027 } 0028 0029 void AbstractBackend::setBackendId(const QString& id) 0030 { 0031 m_backendId = id; 0032 } 0033 0034 QTimeZone AbstractBackend::timeZone() const 0035 { 0036 return m_timeZone; 0037 } 0038 0039 void AbstractBackend::setTimeZone(const QTimeZone &tz) 0040 { 0041 m_timeZone = tz; 0042 } 0043 0044 QStringList AbstractBackend::supportedLanguages() const 0045 { 0046 return m_supportedLanguages; 0047 } 0048 0049 void AbstractBackend::setSupportedLanguages(const QStringList &langs) 0050 { 0051 m_supportedLanguages = langs; 0052 } 0053 0054 QString AbstractBackend::preferredLanguage() const 0055 { 0056 const auto localeLangs = QLocale().uiLanguages(); 0057 for (const auto &l : localeLangs) { 0058 if (m_supportedLanguages.contains(l)) { 0059 return l; 0060 } 0061 if (l.size() > 2 && l[2] == QLatin1Char('-') && m_supportedLanguages.contains(QStringView(l).left(2))) { 0062 return l.left(2); 0063 } 0064 } 0065 0066 if (!m_supportedLanguages.empty()) { 0067 return m_supportedLanguages.at(0); 0068 } 0069 return QStringLiteral("en"); 0070 } 0071 0072 void AbstractBackend::init() 0073 { 0074 } 0075 0076 AbstractBackend::Capabilities AbstractBackend::capabilities() const 0077 { 0078 return NoCapability; 0079 } 0080 0081 bool AbstractBackend::needsLocationQuery(const Location &loc, QueryType type) const 0082 { 0083 Q_UNUSED(loc); 0084 Q_UNUSED(type); 0085 return false; 0086 } 0087 0088 bool AbstractBackend::queryStopover(const StopoverRequest &request, StopoverReply *reply, QNetworkAccessManager *nam) const 0089 { 0090 Q_UNUSED(request); 0091 Q_UNUSED(reply); 0092 Q_UNUSED(nam); 0093 return false; 0094 } 0095 0096 bool AbstractBackend::queryJourney(const JourneyRequest &request, JourneyReply *reply, QNetworkAccessManager *nam) const 0097 { 0098 Q_UNUSED(request); 0099 Q_UNUSED(reply); 0100 Q_UNUSED(nam); 0101 return false; 0102 } 0103 0104 bool AbstractBackend::queryLocation(const LocationRequest &request, LocationReply *reply, QNetworkAccessManager *nam) const 0105 { 0106 Q_UNUSED(request); 0107 Q_UNUSED(reply); 0108 Q_UNUSED(nam); 0109 return false; 0110 } 0111 0112 void AbstractBackend::addAttributions(Reply *reply, std::vector<Attribution> &&attributions) 0113 { 0114 reply->addAttributions(std::move(attributions)); 0115 } 0116 0117 Attribution AbstractBackend::attribution() const 0118 { 0119 return m_attribution; 0120 } 0121 0122 void AbstractBackend::setAttribution(const Attribution &attr) 0123 { 0124 m_attribution = attr; 0125 } 0126 0127 bool AbstractBackend::isLoggingEnabled() const 0128 { 0129 return qEnvironmentVariableIsSet("KPUBLICTRANSPORT_LOG_DIR"); 0130 } 0131 0132 QString AbstractBackend::logDir() const 0133 { 0134 const QString dir = qEnvironmentVariable("KPUBLICTRANSPORT_LOG_DIR") + QLatin1Char('/') + m_backendId + QLatin1Char('/'); 0135 QDir().mkpath(dir); 0136 return dir; 0137 } 0138 0139 void AbstractBackend::logRequest(const char *typeName, const QJsonObject &requestData, const QNetworkRequest &netRequest, const QByteArray &postData) const 0140 { 0141 const QString baseFile = logDir() + QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMddThhmmss.zzz")) + QLatin1Char('-') + QLatin1String(typeName); 0142 0143 if (!postData.isEmpty()) { 0144 QFile dataFile(baseFile + QLatin1String("-3-post-data")); 0145 if (!dataFile.open(QFile::WriteOnly)) { 0146 qCWarning(Log) << "could not open" << dataFile.fileName() << dataFile.errorString(); 0147 return; 0148 } 0149 dataFile.write(postData); 0150 } 0151 0152 QFile httpFile(baseFile + QLatin1String("-2-http-request.txt")); 0153 0154 qCWarning(Log) << "Logging requests to: " << httpFile.fileName(); 0155 0156 if (!httpFile.open(QFile::WriteOnly)) { 0157 qCWarning(Log) << "could not open" << httpFile.fileName() << httpFile.error(); 0158 return; 0159 } 0160 httpFile.write(netRequest.url().toString().toUtf8()); 0161 httpFile.write("\n"); 0162 const auto headers = netRequest.rawHeaderList(); 0163 for (const auto &header : headers) { 0164 httpFile.write(header); 0165 httpFile.write(": "); 0166 httpFile.write(netRequest.rawHeader(header)); 0167 httpFile.write("\n"); 0168 } 0169 0170 QFile reqFile(baseFile + QLatin1String("-1-request.json")); 0171 if (!reqFile.open(QFile::WriteOnly)) { 0172 qCWarning(Log) << "could not open" << reqFile.fileName() << reqFile.error(); 0173 return; 0174 } 0175 reqFile.write(QJsonDocument(requestData).toJson()); 0176 } 0177 0178 void AbstractBackend::logReply(const char *typeName, QNetworkReply *netReply, const QByteArray &data) const 0179 { 0180 const QString baseFile = logDir() + QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMddThhmmss.zzz")) + QLatin1Char('-') + QLatin1String(typeName); 0181 0182 if (!data.isEmpty()) { 0183 const auto contentType = netReply->header(QNetworkRequest::ContentTypeHeader).toString(); 0184 QString ext; 0185 if (contentType == QLatin1String("application/json") || data.startsWith("{")) { 0186 ext = QStringLiteral(".json"); 0187 } else if (contentType == QLatin1String("application/xml") || data.startsWith("<")) { 0188 ext = QStringLiteral(".xml"); 0189 } else if (data.startsWith("\x1f\x8b")) { 0190 ext = QStringLiteral(".gz"); 0191 } 0192 0193 QFile dataFile(baseFile + QLatin1String("-5-reply-data") + ext); 0194 if (!dataFile.open(QFile::WriteOnly)) { 0195 qCWarning(Log) << "could not open" << dataFile.fileName() << dataFile.errorString(); 0196 return; 0197 } 0198 dataFile.write(data); 0199 } 0200 0201 QFile httpFile(baseFile + QLatin1String("-4-http-reply.txt")); 0202 if (!httpFile.open(QFile::WriteOnly)) { 0203 qCWarning(Log) << "could not open" << httpFile.fileName() << httpFile.error(); 0204 return; 0205 } 0206 httpFile.write(netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString().toUtf8()); 0207 httpFile.write(" "); 0208 httpFile.write(netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().toUtf8()); 0209 httpFile.write("\n"); 0210 const auto &headers = netReply->rawHeaderPairs(); 0211 for (const auto &header : headers) { 0212 httpFile.write(header.first); 0213 httpFile.write(": "); 0214 httpFile.write(header.second); 0215 httpFile.write("\n"); 0216 } 0217 } 0218 0219 bool AbstractBackend::queryVehicleLayout(const VehicleLayoutRequest &request, VehicleLayoutReply *reply, QNetworkAccessManager *nam) const 0220 { 0221 Q_UNUSED(request); 0222 Q_UNUSED(reply); 0223 Q_UNUSED(nam); 0224 return false; 0225 } 0226 0227 void AbstractBackend::setCustomCaCertificate(const QString &caCert) 0228 { 0229 QFile f(QLatin1String(":/org.kde.kpublictransport/network-certificates/") + caCert); 0230 if (!f.open(QFile::ReadOnly)) { 0231 qCWarning(Log) << f.fileName() << f.errorString(); 0232 return; 0233 } 0234 m_customCaCerts = QSslCertificate::fromDevice(&f, QSsl::Pem); 0235 } 0236 0237 void AbstractBackend::setPkcs12(const QString &pkcs12Name) 0238 { 0239 QFile f(QLatin1String(":/org.kde.kpublictransport/network-certificates/") + pkcs12Name); 0240 if (!f.open(QFile::ReadOnly)) { 0241 qCWarning(Log) << f.fileName() << f.errorString(); 0242 return; 0243 } 0244 const auto r = QSslCertificate::importPkcs12(&f, &m_privateKey, &m_clientCert, &m_customCaCerts, ""); 0245 if (!r) { 0246 qCWarning(Log) << "Failed to load PKCS#12 bundle" << f.fileName(); 0247 } 0248 } 0249 0250 void AbstractBackend::applySslConfiguration(QNetworkRequest &request) const 0251 { 0252 auto sslConfig = request.sslConfiguration(); 0253 if (!m_customCaCerts.empty()) { 0254 sslConfig.setCaCertificates(m_customCaCerts); 0255 } 0256 if (!m_clientCert.isNull()) { 0257 sslConfig.setLocalCertificate(m_clientCert); 0258 } 0259 if (!m_privateKey.isNull()) { 0260 sslConfig.setPrivateKey(m_privateKey); 0261 } 0262 request.setSslConfiguration(std::move(sslConfig)); 0263 }