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 #ifndef KPUBLICTRANSPORT_ABSTRACTBACKEND_H
0008 #define KPUBLICTRANSPORT_ABSTRACTBACKEND_H
0009 
0010 #include "kpublictransport_export.h"
0011 
0012 #include "reply.h"
0013 #include "requestcontext_p.h"
0014 
0015 #include <KPublicTransport/Attribution>
0016 
0017 #include <QFlags>
0018 #include <QJsonObject>
0019 #include <QPolygonF>
0020 #include <QSslCertificate>
0021 #include <QSslKey>
0022 #include <QTimeZone>
0023 #include <QVariant>
0024 
0025 class QNetworkAccessManager;
0026 class QNetworkReply;
0027 class QNetworkRequest;
0028 
0029 namespace KPublicTransport {
0030 
0031 class JourneyReply;
0032 class JourneyRequest;
0033 class Location;
0034 class LocationReply;
0035 class LocationRequest;
0036 class StopoverReply;
0037 class StopoverRequest;
0038 class VehicleLayoutReply;
0039 class VehicleLayoutRequest;
0040 
0041 /** Abstract base class for transport provider backends.
0042  *  @internal exported for tooling only
0043  */
0044 class KPUBLICTRANSPORT_EXPORT AbstractBackend
0045 {
0046     Q_GADGET
0047     Q_PROPERTY(QString customCaCertificate WRITE setCustomCaCertificate)
0048     /** PKCS#12 client certificate bundle. */
0049     Q_PROPERTY(QString pkcs12 WRITE setPkcs12)
0050 public:
0051     AbstractBackend();
0052     virtual ~AbstractBackend();
0053 
0054     /** Identifier for this backend.
0055      *  Use e.g. for distinguishing backend-specific cache locations etc.
0056      */
0057     QString backendId() const;
0058     void setBackendId(const QString &id);
0059 
0060     /** Static timezone for all times used by and provided by the backend.
0061      *  Set to invalid if the backend supports multiple timezones.
0062      */
0063     QTimeZone timeZone() const;
0064     void setTimeZone(const QTimeZone &tz);
0065 
0066     /** Supported languages.
0067      *  This matters for backends that require us to ask for humand readable strings
0068      *  in a specific language (as opposed to providing all possible translations by default).
0069      *  The first language in QLocale::uiLanguages found in this list should be requested.
0070      *  @see preferredLanguage()
0071      */
0072     QStringList supportedLanguages() const;
0073     void setSupportedLanguages(const QStringList &langs);
0074 
0075     /** Returns the language that should be requested.
0076      *  This is essentially the first language in QLocale::uiLanguages that is also found
0077      *  in the list of supported languages, if that's not the case the first supported
0078      *  language of the backend, or if that's not set, English by default.
0079      */
0080     QString preferredLanguage() const;
0081 
0082     /** Called after all settings have been set on this backend. */
0083     virtual void init();
0084 
0085     enum Capability {
0086         NoCapability = 0,
0087         Secure = 1, ///< uses transport encryption
0088         CanQueryNextJourney = 2, ///< supports querying for later journeys
0089         CanQueryPreviousJourney = 4, ///< supports query for earlier journeys
0090         CanQueryNextDeparture = 8, ///< supports querying for later departures
0091         CanQueryPreviousDeparture = 16, ///< supports query for earlier departures
0092         CanQueryArrivals = 32, ///< supports querying arrival stopovers at a given location
0093     };
0094     Q_DECLARE_FLAGS(Capabilities, Capability)
0095 
0096     /** Returns the capabilities of this backend. */
0097     virtual Capabilities capabilities() const;
0098     /** Checks if this backend has @p capability. */
0099     inline bool hasCapability(Capability capability) const { return capabilities() & capability; };
0100 
0101     /** Returns the static attribution information for this backend. */
0102     Attribution attribution() const;
0103     /** Sets the static attribution information for this backend. */
0104     void setAttribution(const Attribution &attr);
0105 
0106     /** Type of query.
0107      *  @see needsLocationQuery
0108      */
0109     enum class QueryType {
0110         Departure,
0111         Journey
0112     };
0113     /** Returns whether or not a location query has to be performed for the given
0114      *  Location before doing departure or journey queries for it.
0115      */
0116     virtual bool needsLocationQuery(const Location &loc, QueryType type) const;
0117 
0118     /** Perform a journey query.
0119      *  @return @c true if performing an async operation, @c false otherwise.
0120      */
0121     virtual bool queryJourney(const JourneyRequest &request, JourneyReply *reply, QNetworkAccessManager *nam) const;
0122 
0123     /** Perform a departure query.
0124      *  @return @c true if performing an async operation, @c false otherwise.
0125      */
0126     virtual bool queryStopover(const StopoverRequest &request, StopoverReply *reply, QNetworkAccessManager *nam) const;
0127 
0128     /** Perform a location query.
0129      *  @return @c true if performing an async operation, @c false otherwise.
0130      */
0131     virtual bool queryLocation(const LocationRequest &request, LocationReply *reply, QNetworkAccessManager *nam) const;
0132 
0133     /** Perform a vehicle layout query.
0134      *  @return @c true if performing an async operation, @c false otherwise.
0135      */
0136     virtual bool queryVehicleLayout(const VehicleLayoutRequest &request, VehicleLayoutReply *reply, QNetworkAccessManager *nam) const;
0137 
0138 protected:
0139     /** Helper function to call non-public Reply API. */
0140     template <typename T, typename ...Args> inline static void addResult(T *reply, Args&&... args)
0141     {
0142         reply->addResult(std::forward<Args>(args)...);
0143     }
0144 
0145     template <typename T> inline void addError(T *reply, Reply::Error error, const QString &errorMsg) const
0146     {
0147         reply->addError(this, error, errorMsg);
0148     }
0149 
0150     static void addAttributions(Reply *reply, std::vector<Attribution> &&attributions);
0151 
0152     /** Extract the request context for the current backend from @p request. */
0153     template <typename ReqT> inline RequestContext requestContext(const ReqT &request) const
0154     {
0155         return request.context(this);
0156     }
0157     /** Extract the backend-specific data from the request context for the current backend from @p request. */
0158     template <typename ReqT> inline QVariant requestContextData(const ReqT &request) const
0159     {
0160         return request.context(this).backendData;
0161     }
0162 
0163     template <typename RepT> inline void setNextRequestContext(RepT *reply, const QVariant &data) const
0164     {
0165         reply->setNextContext(this, data);
0166     }
0167     template <typename RepT> inline void setPreviousRequestContext(RepT *reply, const QVariant &data) const
0168     {
0169         reply->setPreviousContext(this, data);
0170     }
0171 
0172     /** Check if network logging is enabled.
0173      *  Done automatically in logRequest/logReply, but can be useful for backends if producing
0174      *  the necessary input is expensive.
0175      */
0176     bool isLoggingEnabled() const;
0177 
0178     template <typename ReqT>
0179     void logRequest(const ReqT &request, const QNetworkRequest &netRequest, const QByteArray &postData = {}) const
0180     {
0181         if (!isLoggingEnabled()) {
0182             return;
0183         }
0184 
0185         logRequest(ReqT::staticMetaObject.className() + 18, ReqT::toJson(request), netRequest, postData);
0186     }
0187 
0188     template <typename RepT>
0189     void logReply(RepT *reply, QNetworkReply *netReply, const QByteArray &data) const
0190     {
0191         if (!isLoggingEnabled()) {
0192             return;
0193         }
0194 
0195         logReply(reply->metaObject()->className() + 18, netReply, data);
0196     }
0197 
0198     /** Apply custom SSL workaround on the given network request. */
0199     void applySslConfiguration(QNetworkRequest &request) const;
0200 
0201 private:
0202     Q_DISABLE_COPY(AbstractBackend)
0203     QString logDir() const;
0204     void logRequest(const char *typeName, const QJsonObject &requestData, const QNetworkRequest &netRequest, const QByteArray &postData) const;
0205     void logReply(const char *typeName, QNetworkReply *netReply, const QByteArray &data) const;
0206     void setCustomCaCertificate(const QString &caCert);
0207     void setPkcs12(const QString &pkcs12Name);
0208 
0209     QString m_backendId;
0210     Attribution m_attribution;
0211     QTimeZone m_timeZone;
0212     QStringList m_supportedLanguages;
0213     QList<QSslCertificate> m_customCaCerts;
0214     QSslCertificate m_clientCert;
0215     QSslKey m_privateKey;
0216 };
0217 
0218 Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractBackend::Capabilities)
0219 
0220 class AbstractAsyncTask : public QObject
0221 {
0222     Q_OBJECT
0223 
0224 public:
0225     AbstractAsyncTask(QObject *parent = nullptr) : QObject(parent) {}
0226 
0227     Q_SIGNAL void finished();
0228 };
0229 
0230 ///
0231 /// Helper to return a value asynchronously
0232 ///
0233 template <typename T>
0234 class AsyncTask : public AbstractAsyncTask
0235 {
0236     std::optional<T> m_result;
0237 
0238 public:
0239     using AbstractAsyncTask::AbstractAsyncTask;
0240 
0241     void reportFinished(T &&result) {
0242         m_result = std::move(result);
0243         Q_EMIT finished();
0244     }
0245 
0246     const std::optional<T> &result() const {
0247         return m_result;
0248     }
0249 };
0250 
0251 template <>
0252 class AsyncTask<void> : public AbstractAsyncTask
0253 {
0254 public:
0255     using AbstractAsyncTask::AbstractAsyncTask;
0256 
0257     void reportFinished() {
0258         Q_EMIT finished();
0259     }
0260 };
0261 
0262 }
0263 
0264 #endif // KPUBLICTRANSPORT_ABSTRACTBACKEND_H