8#include "assetrepository_p.h"
9#include "backends/srbijavozbackend.h"
10#include "backends/zpcgbackend.h"
11#include "journeyreply.h"
12#include "journeyrequest.h"
13#include "requestcontext_p.h"
14#include "locationreply.h"
15#include "locationrequest.h"
17#include "stopoverreply.h"
18#include "stopoverrequest.h"
19#include "vehiclelayoutrequest.h"
20#include "vehiclelayoutreply.h"
21#include "datatypes/attributionutil_p.h"
22#include "datatypes/backend.h"
23#include "datatypes/backend_p.h"
24#include "datatypes/disruption.h"
25#include "datatypes/json_p.h"
26#include "datatypes/platform.h"
27#include "datatypes/vehicle.h"
28#include "geo/geojson_p.h"
30#include <KPublicTransport/Journey>
31#include <KPublicTransport/Location>
32#include <KPublicTransport/Stopover>
34#include "backends/accessibilitycloudbackend.h"
35#include "backends/cache.h"
36#include "backends/deutschebahnbackend.h"
37#include "backends/efabackend.h"
38#include "backends/hafasmgatebackend.h"
39#include "backends/hafasquerybackend.h"
40#include "backends/ivvassbackend.h"
41#include "backends/motisbackend.h"
42#include "backends/navitiabackend.h"
43#include "backends/oebbbackend.h"
44#include "backends/openjourneyplannerbackend.h"
45#include "backends/opentripplannergraphqlbackend.h"
46#include "backends/opentripplannerrestbackend.h"
47#include "backends/pasazieruvilciensbackend.h"
48#include "backends/ltglinkbackend.h"
49#include "gbfs/gbfsbackend.h"
51#include <QDirIterator>
53#include <QJsonDocument>
55#include <QMetaProperty>
56#include <QNetworkAccessManager>
57#include <QStandardPaths>
66static inline void initResources() {
67 Q_INIT_RESOURCE(asset_attributions);
68 Q_INIT_RESOURCE(gbfs);
69 Q_INIT_RESOURCE(geometry);
70 Q_INIT_RESOURCE(networks);
71 Q_INIT_RESOURCE(network_certs);
73 Q_INIT_RESOURCE(stations);
81 std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &obj);
82 template <
typename Backend,
typename Backend2,
typename ...Backends>
83 static std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj);
84 template <
typename Backend> std::unique_ptr<AbstractBackend>
87 static std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &obj);
89 template <
typename RequestT>
bool shouldSkipBackend(
const Backend &backend,
const RequestT &req)
const;
91 void resolveLocation(
LocationRequest &&locReq,
const AbstractBackend *backend,
const std::function<
void(
const Location &loc)> &callback);
95 template <
typename RepT,
typename ReqT> RepT* makeReply(
const ReqT &request);
97 void readCachedAttributions();
103 std::vector<Backend> m_backends;
104 std::vector<Attribution> m_attributions;
110 bool m_allowInsecure =
false;
111 bool m_hasReadCachedAttributions =
false;
112 bool m_backendsEnabledByDefault =
true;
115 bool shouldSkipBackend(
const Backend &backend)
const;
131void ManagerPrivate::loadNetworks()
133 if (!m_backends.empty()) {
143 for (
const auto &searchDir : searchDirs) {
145 while (it.hasNext()) {
147 const auto id = it.fileInfo().baseName();
148 if (std::any_of(m_backends.begin(), m_backends.end(), [&
id](
const auto &backend) { return backend.identifier() == id; })) {
153 QFile f(it.filePath());
155 qCWarning(
Log) <<
"Failed to open public transport network configuration:" << f.errorString();
162 qCWarning(
Log) <<
"Failed to parse public transport network configuration:" <<
error.errorString() << it.fileName();
166 auto net = loadNetwork(doc.object());
168 net->setBackendId(
id);
170 if (!net->attribution().isEmpty()) {
171 m_attributions.push_back(net->attribution());
174 auto b = BackendPrivate::fromJson(doc.object());
175 BackendPrivate::setImpl(b, std::move(net));
176 m_backends.push_back(std::move(b));
178 qCWarning(
Log) <<
"Failed to load public transport network configuration config:" << it.fileName();
183 std::stable_sort(m_backends.begin(), m_backends.end(), [](
const auto &lhs,
const auto &rhs) {
184 return lhs.identifier() < rhs.identifier();
187 AttributionUtil::sort(m_attributions);
188 qCDebug(
Log) << m_backends.size() <<
"public transport network configurations loaded";
191std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &obj)
197 OpenTripPlannerGraphQLBackend,
198 OpenTripPlannerRestBackend,
205 OpenJourneyPlannerBackend,
208 AccessibilityCloudBackend,
209 PasazieruVilciensBackend,
216template <
typename Backend,
typename Backend2,
typename ...Backends>
217std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj)
220 return loadNetwork<Backend>(obj);
222 return loadNetwork<Backend2, Backends...>(backendType, obj);
225template <
typename Backend>
226std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj)
229 return ManagerPrivate::loadNetwork<Backend>(obj);
231 qCWarning(
Log) <<
"Unknown backend type:" << backendType;
238 for (
auto it = opts.begin(); it != opts.end(); ++it) {
241 qCWarning(
Log) <<
"Unknown backend setting:" << it.key();
245 if (it.value().isObject()) {
247 }
else if (it.value().isArray()) {
248 const auto a = it.value().toArray();
252 std::transform(a.begin(), a.end(), std::back_inserter(l), [](
const auto &v) { return v.toString(); });
253 mp.writeOnGadget(backend, l);
255 mp.writeOnGadget(backend, it.value().toArray());
258 mp.writeOnGadget(backend, it.value().toVariant());
264 backend->setAttribution(attr);
267 if (!tzId.isEmpty()) {
270 backend->setTimeZone(tz);
272 qCWarning(
Log) <<
"Invalid timezone:" << tzId;
278 langs.
reserve(langArray.size());
279 std::transform(langArray.begin(), langArray.end(), std::back_inserter(langs), [](
const auto &v) { return v.toString(); });
280 backend->setSupportedLanguages(langs);
283template<
typename T> std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &obj)
285 std::unique_ptr<AbstractBackend> backend(
new T);
286 applyBackendOptions(backend.get(), &T::staticMetaObject, obj);
290bool ManagerPrivate::shouldSkipBackend(
const Backend &backend)
const
292 if (!backend.
isSecure() && !m_allowInsecure) {
293 qCDebug(
Log) <<
"Skipping insecure backend:" << backend.
identifier();
299template <
typename RequestT>
300bool ManagerPrivate::shouldSkipBackend(
const Backend &backend,
const RequestT &req)
const
302 if (!req.backendIds().isEmpty() && !req.backendIds().contains(backend.
identifier())) {
306 return shouldSkipBackend(backend);
311void ManagerPrivate::resolveLocation(
LocationRequest &&locReq,
const AbstractBackend *backend,
const std::function<
void(
const Location&)> &callback)
314 locReq.setMaximumResults(1);
317 const auto cacheEntry = Cache::lookupLocation(backend->backendId(), locReq.cacheKey());
318 switch (cacheEntry.type) {
319 case CacheHitType::Negative:
322 case CacheHitType::Positive:
323 if (!cacheEntry.data.empty()) {
324 const auto loc = cacheEntry.data[0];
329 case CacheHitType::Miss:
335 if (backend->queryLocation(locReq, locReply, nam())) {
336 locReply->setPendingOps(1);
338 locReply->setPendingOps(0);
341 locReply->deleteLater();
342 if (locReply->result().empty()) {
345 callback(locReply->result()[0]);
353 if (req.
modes() & JourneySection::PublicTransport) {
364 auto cache = Cache::lookupJourney(backend->backendId(), req.
cacheKey());
365 switch (cache.type) {
366 case CacheHitType::Negative:
367 qCDebug(
Log) <<
"Negative cache hit for backend" << backend->backendId();
369 case CacheHitType::Positive:
370 qCDebug(
Log) <<
"Positive cache hit for backend" << backend->backendId();
371 reply->addAttributions(std::move(cache.attributions));
372 reply->addResult(backend, std::move(cache.data));
374 case CacheHitType::Miss:
375 qCDebug(
Log) <<
"Cache miss for backend" << backend->backendId();
380 if (backend->needsLocationQuery(req.
from(), AbstractBackend::QueryType::Journey)) {
382 fromReq.setTypes(locationTypesForJourneyRequest(req));
383 resolveLocation(std::move(fromReq), backend, [reply, backend, req,
this](
const Location &loc) {
384 auto jnyRequest = req;
386 jnyRequest.setFrom(fromLoc);
388 if (backend->needsLocationQuery(jnyRequest.to(), AbstractBackend::QueryType::Journey)) {
390 toReq.setTypes(locationTypesForJourneyRequest(req));
391 resolveLocation(std::move(toReq), backend, [jnyRequest, reply, backend,
this](
const Location &loc) {
392 auto jnyReq = jnyRequest;
395 if (!backend->queryJourney(jnyReq, reply, nam())) {
403 if (!backend->queryJourney(jnyRequest, reply, nam())) {
411 if (backend->needsLocationQuery(req.
to(), AbstractBackend::QueryType::Journey)) {
413 toReq.setTypes(locationTypesForJourneyRequest(req));
414 resolveLocation(std::move(toReq), backend, [req, toReq, reply, backend,
this](
const Location &loc) {
416 auto jnyRequest = req;
417 jnyRequest.setTo(toLoc);
418 if (!backend->queryJourney(jnyRequest, reply, nam())) {
425 return backend->queryJourney(req, reply, nam());
430 auto cache = Cache::lookupStopover(backend->backendId(), req.
cacheKey());
431 switch (cache.type) {
432 case CacheHitType::Negative:
433 qCDebug(
Log) <<
"Negative cache hit for backend" << backend->backendId();
435 case CacheHitType::Positive:
436 qCDebug(
Log) <<
"Positive cache hit for backend" << backend->backendId();
437 reply->addAttributions(std::move(cache.attributions));
438 reply->addResult(backend, std::move(cache.data));
440 case CacheHitType::Miss:
441 qCDebug(
Log) <<
"Cache miss for backend" << backend->backendId();
446 if (backend->needsLocationQuery(req.
stop(), AbstractBackend::QueryType::Departure)) {
447 qCDebug(
Log) <<
"Backend needs location query first:" << backend->backendId();
450 resolveLocation(std::move(locReq), backend, [reply, req, backend,
this](
const Location &loc) {
452 auto depRequest = req;
453 depRequest.setStop(depLoc);
454 if (!backend->queryStopover(depRequest, reply, nam())) {
461 return backend->queryStopover(req, reply, nam());
464void ManagerPrivate::readCachedAttributions()
466 if (m_hasReadCachedAttributions) {
470 Cache::allCachedAttributions(m_attributions);
471 m_hasReadCachedAttributions =
true;
474template<
typename RepT,
typename ReqT>
475RepT* ManagerPrivate::makeReply(
const ReqT &request)
477 auto reply =
new RepT(request, q);
479 AttributionUtil::merge(m_attributions, reply->
attributions());
486Manager::Manager(
QObject *parent)
488 , d(new ManagerPrivate)
491 qRegisterMetaType<Disruption::Effect>();
494 if (!AssetRepository::instance()) {
495 auto assetRepo =
new AssetRepository(
this);
496 assetRepo->setNetworkAccessManagerProvider(std::bind(&ManagerPrivate::nam, d.get()));
502Manager::~Manager() =
default;
506 if (d->m_nam == nam) {
510 if (d->m_nam && d->m_nam->parent() ==
this) {
517bool Manager::allowInsecureBackends()
const
519 return d->m_allowInsecure;
522void Manager::setAllowInsecureBackends(
bool insecure)
524 if (d->m_allowInsecure == insecure) {
527 d->m_allowInsecure = insecure;
528 Q_EMIT configurationChanged();
540 reply->setPendingOps(pendingOps);
547 if (req.contexts().empty()) {
549 bool foundNonGlobalCoverage =
false;
550 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
551 const auto checkBackend = [&](
const Backend &backend,
bool bothLocationMatch) {
552 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
555 const auto coverage = backend.coverageArea(coverageType);
556 if (coverage.isEmpty()) {
560 if (bothLocationMatch) {
561 if (!coverage.coversLocation(req.
from()) || !coverage.coversLocation(req.
to())) {
565 if (!coverage.coversLocation(req.
from()) && !coverage.coversLocation(req.
to())) {
571 foundNonGlobalCoverage |= !coverage.isGlobal();
573 if (d->queryJourney(BackendPrivate::impl(backend), req, reply)) {
579 for (
const auto &backend: d->m_backends) {
580 checkBackend(backend,
true);
582 if (pendingOps && foundNonGlobalCoverage) {
587 for (
const auto &backend: d->m_backends) {
588 checkBackend(backend,
false);
590 if (pendingOps && foundNonGlobalCoverage) {
597 for (
const auto &context : req.contexts()) {
599 if ((context.type == RequestContext::Next && context.backend->hasCapability(AbstractBackend::CanQueryNextJourney))
600 ||(context.type == RequestContext::Previous && context.backend->hasCapability(AbstractBackend::CanQueryPreviousJourney)))
602 if (d->queryJourney(context.backend, req, reply)) {
612 if (d->queryJourney(context.backend, r, reply)) {
619 if (d->queryJourney(context.backend, r, reply)) {
628 reply->addAttributions(AssetRepository::instance()->attributions());
630 reply->setPendingOps(pendingOps);
642 reply->setPendingOps(pendingOps);
649 if (req.contexts().empty()) {
651 bool foundNonGlobalCoverage =
false;
652 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
653 for (
const auto &backend: d->m_backends) {
654 if (triedBackends.
contains(backend.identifier()) || d->shouldSkipBackend(backend, req)) {
658 qCDebug(
Log) <<
"Skipping backend due to not supporting arrival queries:" << backend.identifier();
661 const auto coverage = backend.coverageArea(coverageType);
662 if (coverage.isEmpty() || !coverage.coversLocation(req.
stop())) {
665 triedBackends.
insert(backend.identifier());
666 foundNonGlobalCoverage |= !coverage.isGlobal();
668 if (d->queryStopover(BackendPrivate::impl(backend), req, reply)) {
673 if (pendingOps && foundNonGlobalCoverage) {
680 for (
const auto &context : req.contexts()) {
682 if ((context.type == RequestContext::Next && context.backend->hasCapability(AbstractBackend::CanQueryNextDeparture))
683 ||(context.type == RequestContext::Previous && context.backend->hasCapability(AbstractBackend::CanQueryPreviousDeparture)))
685 if (d->queryStopover(context.backend, req, reply)) {
692 if (context.type == RequestContext::Next) {
694 r.setDateTime(context.dateTime);
695 if (d->queryStopover(context.backend, r, reply)) {
704 reply->addAttributions(AssetRepository::instance()->attributions());
706 reply->setPendingOps(pendingOps);
713 switch (cache.type) {
714 case CacheHitType::Negative:
715 qCDebug(
Log) <<
"Negative cache hit for backend" << backend.
identifier();
717 case CacheHitType::Positive:
718 qCDebug(
Log) <<
"Positive cache hit for backend" << backend.
identifier();
719 reply->addAttributions(std::move(cache.attributions));
720 reply->addResult(std::move(cache.data));
722 case CacheHitType::Miss:
723 qCDebug(
Log) <<
"Cache miss for backend" << backend.
identifier();
724 reply->addAttribution(BackendPrivate::impl(backend)->attribution());
725 if (BackendPrivate::impl(backend)->queryLocation(req, reply, nam())) {
742 reply->setPendingOps(pendingOps);
749 bool foundNonGlobalCoverage =
false;
752 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
754 for (
const auto &backend : d->m_backends) {
755 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
758 const auto coverage = backend.coverageArea(coverageType);
759 if (coverage.isEmpty() || !coverage.coversLocation(loc)) {
762 if (isCountryOnly && !coverage.hasNationWideCoverage(loc.
country())) {
767 foundNonGlobalCoverage |= !coverage.isGlobal();
768 pendingOps += d->queryLocationOnBackend(req, reply, backend);
770 if (pendingOps && foundNonGlobalCoverage) {
775 for (
const auto &backend : d->m_backends) {
776 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
779 const auto coverage = backend.coverageArea(coverageType);
780 if (coverage.isEmpty() || !coverage.coversLocation(loc)) {
785 foundNonGlobalCoverage |= !coverage.isGlobal();
786 pendingOps += d->queryLocationOnBackend(req, reply, backend);
788 if (pendingOps && foundNonGlobalCoverage) {
792 reply->setPendingOps(pendingOps);
804 reply->setPendingOps(pendingOps);
810 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular }) {
811 for (
const auto &backend : d->m_backends) {
812 if (d->shouldSkipBackend(backend, req)) {
815 const auto coverage = backend.coverageArea(coverageType);
816 if (coverage.isEmpty() || !coverage.coversLocation(req.
stopover().
stopPoint())) {
819 reply->addAttribution(BackendPrivate::impl(backend)->attribution());
822 switch (cache.type) {
823 case CacheHitType::Negative:
824 qCDebug(
Log) <<
"Negative cache hit for backend" << backend.
identifier();
826 case CacheHitType::Positive:
827 qCDebug(
Log) <<
"Positive cache hit for backend" << backend.
identifier();
828 if (cache.data.size() == 1) {
829 reply->addAttributions(std::move(cache.attributions));
830 reply->addResult(cache.data[0]);
834 case CacheHitType::Miss:
835 qCDebug(
Log) <<
"Cache miss for backend" << backend.
identifier();
847 reply->setPendingOps(pendingOps);
854 d->readCachedAttributions();
855 return d->m_attributions;
858QVariantList Manager::attributionsVariant()
const
861 d->readCachedAttributions();
863 l.
reserve(d->m_attributions.size());
864 std::transform(d->m_attributions.begin(), d->m_attributions.end(), std::back_inserter(l), [](
const auto &attr) { return QVariant::fromValue(attr); });
871 return d->m_backends;
876 if (std::binary_search(d->m_disabledBackends.cbegin(), d->m_disabledBackends.cend(), backendId)) {
879 if (std::binary_search(d->m_enabledBackends.cbegin(), d->m_enabledBackends.cend(), backendId)) {
883 return d->m_backendsEnabledByDefault;
888 const auto it = std::lower_bound(l.
begin(), l.
end(), value);
889 if (it == l.
end() || (*it) != value) {
896 const auto it = std::lower_bound(l.
begin(), l.
end(), value);
897 if (it != l.
end() && (*it) == value) {
905 sortedInsert(d->m_enabledBackends, backendId);
906 sortedRemove(d->m_disabledBackends, backendId);
908 sortedRemove(d->m_enabledBackends, backendId);
909 sortedInsert(d->m_disabledBackends, backendId);
911 Q_EMIT configurationChanged();
916 return d->m_enabledBackends;
922 for (
const auto &backendId : backendIds) {
929 return d->m_disabledBackends;
935 for (
const auto &backendId : backendIds) {
942 return d->m_backendsEnabledByDefault;
947 d->m_backendsEnabledByDefault = byDefault;
949 Q_EMIT configurationChanged();
952QVariantList Manager::backendsVariant()
const
956 l.
reserve(d->m_backends.size());
957 std::transform(d->m_backends.begin(), d->m_backends.end(), std::back_inserter(l), [](
const auto &b) { return QVariant::fromValue(b); });
static Attribution fromJson(const QJsonObject &obj)
Deserialize an Attribution object from JSON.
Information about a backend service queried for location/departure/journey data.
bool isSecure
Supports secrure network access.
QString identifier
Internal identifier of this backend.
Describes a journey search.
KPublicTransport::Location to
The journey destination.
void setArrivalTime(const QDateTime &dt)
Sets the desired arrival time.
void setDepartureTime(const QDateTime &dt)
Set the desired departure time.
bool downloadAssets
Download graphic assets such as line logos for the data requested here.
@ Departure
dateTime() represents the desired departure time.
bool isValid() const
Returns true if this is a valid request, that is, it has enough parameters set to perform a query.
QString cacheKey() const
Unique string representation used for caching results.
KPublicTransport::Location from
The starting point of the journey search.
KPublicTransport::JourneySection::Modes modes
Modes of transportation that should be considered for this query.
DateTimeMode dateTimeMode
Controls whether to search for journeys starting or ending at the given time.
@ RentedVehicle
free floating or dock-based rental bike service, electric scooters, car sharing services,...
Describes a location search.
bool isValid() const
Returns true if this is a valid request, that is it has enough parameters set to perform a query.
QString cacheKey() const
Unique string representation used for caching results.
KPublicTransport::Location location
Location object containing the search parameters.
@ RentedVehicleStation
a pick-up/drop-off point for dock-based rental bike/scooter systems
@ Place
a location that isn't of any specific type
@ Stop
a public transport stop (train station, bus stop, etc)
QString country
Country of the location as ISO 3166-1 alpha 2 code, if known.
static Location merge(const Location &lhs, const Location &rhs)
Merge two departure instances.
QString region
Region (as in ISO 3166-2) of the location, if known.
LocationReply * queryLocation(const LocationRequest &req) const
Query location information based on coordinates or (parts of) the name.
void setBackendsEnabledByDefault(bool byDefault)
Set wheter backends are enabled by default.
void setEnabledBackends(const QStringList &backendIds)
Sets the explicitly enabled backends.
bool backendsEnabledByDefault
void setBackendEnabled(const QString &backendId, bool enabled)
Sets whether the backend with the given identifier should be used.
QStringList disabledBackends
Q_INVOKABLE bool isBackendEnabled(const QString &backendId) const
Returns whether the use of the backend with a given identifier is enabled.
void setDisabledBackends(const QStringList &backendIds)
Sets the explicitly disabled backends.
VehicleLayoutReply * queryVehicleLayout(const VehicleLayoutRequest &req) const
Query vehicle and platform layout information.
QVariantList backends
QML-compatible access to backends().
QStringList enabledBackends
QVariantList attributions
QML-compatible access to attributions().
void finished()
Emitted whenever the corresponding search has been completed.
const std::vector< Attribution > & attributions() const
Returns the attributions for the provided data.
@ InvalidRequest
Incomplete or otherwise invalid request.
@ NotFoundError
The requested journey/departure/place could not be found.
Departure or arrival query reply.
Describes an arrival or departure search.
@ QueryArrival
Search for arrivals.
bool downloadAssets
Enable downloading of graphic assets such as line logos for the data requested here.
bool isValid() const
Returns true if this is a valid request, ie.
QString cacheKey() const
Unique string representation used for caching results.
KPublicTransport::Location stop
The location at which to search for departures/arrivals.
Mode mode
Controls whether to search for arrivals or departures.
KPublicTransport::Location stopPoint
The stop point of this departure.
Reply to a vehicle layout query.
Describes a query for vehicle layout information.
QString cacheKey() const
Unique string representation used for caching results.
bool isValid() const
Returns true if this is a valid request, that is it has enough parameters set to perform a query.
KPublicTransport::Stopover stopover
The stopover vehicle and platform layout information are requested for.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Query operations and data types for accessing realtime public transport information from online servi...
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonValue value(QLatin1StringView key) const const
QJsonArray toArray() const const
QJsonObject toObject() const const
QString toString() const const
iterator erase(const_iterator begin, const_iterator end)
iterator insert(const_iterator before, parameter_type value)
void push_back(parameter_type value)
void reserve(qsizetype size)
void enableStrictTransportSecurityStore(bool enabled, const QString &storeDir)
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy)
void setStrictTransportSecurityEnabled(bool enabled)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
QStringList standardLocations(StandardLocation type)
QString writableLocation(StandardLocation type)
bool isEmpty() const const