diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/io/qfsfileengine.cpp | 4 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 34 | ||||
-rw-r--r-- | src/corelib/thread/qthread.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.cpp | 33 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.h | 4 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool_p.h | 12 | ||||
-rw-r--r-- | src/corelib/tools/qbytearray.cpp | 15 | ||||
-rw-r--r-- | src/network/access/access.pri | 6 | ||||
-rw-r--r-- | src/network/access/qhsts.cpp | 61 | ||||
-rw-r--r-- | src/network/access/qhsts_p.h | 7 | ||||
-rw-r--r-- | src/network/access/qhstsstore.cpp | 202 | ||||
-rw-r--r-- | src/network/access/qhstsstore_p.h | 93 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.cpp | 44 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.h | 3 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager_p.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.cpp | 7 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.h | 10 |
17 files changed, 489 insertions, 50 deletions
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 037deb8942..b7a5440224 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -923,10 +923,6 @@ bool QFSFileEngine::supportsExtension(Extension extension) const \reimp */ -/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const - \reimp -*/ - /*! \fn QString QFSFileEngine::homePath() Returns the home path of the current user. diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 29af0647ea..b73c83bc49 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1459,7 +1459,11 @@ Q_CORE_EXPORT void QVariantPrivate::registerHandler(const int /* Modules::Names /*! \fn QVariant::QVariant(Type type) - Constructs a null variant of type \a type. + Constructs an uninitialized variant of type \a type. This will create a + variant in a special null state that if accessed will return a default + constructed value of the \a type. + + \sa isNull() */ @@ -3305,17 +3309,20 @@ bool QVariant::canConvert(int targetTypeId) const /*! Casts the variant to the requested type, \a targetTypeId. If the cast cannot be - done, the variant is cleared. Returns \c true if the current type of - the variant was successfully cast; otherwise returns \c false. + done, the variant is still changed to the requested type, but is left in a cleared + null state similar to that constructed by QVariant(Type). + + Returns \c true if the current type of the variant was successfully cast; + otherwise returns \c false. A QVariant containing a pointer to a type derived from QObject will also convert and return true for this function if a qobject_cast to the type described by \a targetTypeId would succeed. Note that this only works for QObject subclasses which use the Q_OBJECT macro. - \warning For historical reasons, converting a null QVariant results - in a null value of the desired type (e.g., an empty string for - QString) and a result of false. + \note converting QVariants that are null due to not being initialized or having + failed a previous conversion will always fail, changing the type, remaining null, + and returning \c false. \sa canConvert(), clear() */ @@ -3332,7 +3339,8 @@ bool QVariant::convert(int targetTypeId) return false; create(targetTypeId, 0); - if (oldValue.isNull()) + // Fail if the value is not initialized or was forced null by a previous failed convert. + if (oldValue.d.is_null) return false; if ((QMetaType::typeFlags(oldValue.userType()) & QMetaType::PointerToQObject) && (QMetaType::typeFlags(targetTypeId) & QMetaType::PointerToQObject)) { @@ -3732,12 +3740,14 @@ void* QVariant::data() /*! Returns \c true if this is a null variant, false otherwise. A variant is - considered null if it contains a default constructed value or a built-in - type instance that has an isNull method, in which case the result - would be the same as calling isNull on the wrapped object. + considered null if it contains no initialized value or it contains an instance + of built-in type that has an isNull method, in which case the result would be + the same as calling isNull on the wrapped object. + + \warning Null variants is not a single state and two null variants may easily + return \c false on the == operator if they do not contain similar null values. - \warning The result of the function doesn't affect == operator, which means - that two values can be equal even if one of them is null and another is not. + \sa QVariant(Type), convert(int) */ bool QVariant::isNull() const { diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index b662475003..1ec626a53b 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -291,7 +291,7 @@ QThreadPrivate::~QThreadPrivate() \fn int QThread::idealThreadCount() Returns the ideal number of threads that can be run on the system. This is done querying - the number of processor cores, both real and logical, in the system. This function returns -1 + the number of processor cores, both real and logical, in the system. This function returns 1 if the number of processor cores could not be detected. */ diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index f3ce1f258f..9937201619 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -74,7 +74,9 @@ public: */ QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager) :manager(manager), runnable(0) -{ } +{ + setStackSize(manager->stackSize); +} /* \internal @@ -154,11 +156,6 @@ void QThreadPoolThread::registerThreadInactive() \internal */ QThreadPoolPrivate:: QThreadPoolPrivate() - : isExiting(false), - expiryTimeout(30000), - maxThreadCount(qAbs(QThread::idealThreadCount())), - reservedThreads(0), - activeThreads(0) { } bool QThreadPoolPrivate::tryStart(QRunnable *task) @@ -609,6 +606,30 @@ void QThreadPool::reserveThread() ++d->reservedThreads; } +/*! \property QThreadPool::stacksize + + This property contains the stack size for the thread pool worker + threads. + + The value of the property is uses when the thread pool creates + new threads only. Changing it has no effect for already created + or running threads. + + The default value is 0, which makes QThread use the operating + system default stack stize. +*/ +void QThreadPool::setStackSize(uint stackSize) +{ + Q_D(QThreadPool); + d->stackSize = stackSize; +} + +uint QThreadPool::stackSize() const +{ + Q_D(const QThreadPool); + return d->stackSize; +} + /*! Releases a thread previously reserved by a call to reserveThread(). diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h index 09b7f96f48..a65eacc996 100644 --- a/src/corelib/thread/qthreadpool.h +++ b/src/corelib/thread/qthreadpool.h @@ -58,6 +58,7 @@ class Q_CORE_EXPORT QThreadPool : public QObject Q_PROPERTY(int expiryTimeout READ expiryTimeout WRITE setExpiryTimeout) Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount) Q_PROPERTY(int activeThreadCount READ activeThreadCount) + Q_PROPERTY(uint stackSize READ stackSize WRITE setStackSize) friend class QFutureInterfaceBase; public: @@ -77,6 +78,9 @@ public: int activeThreadCount() const; + void setStackSize(uint stackSize); + uint stackSize() const; + void reserveThread(); void releaseThread(); diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h index 4a9f9e5cfa..8b6a8cc476 100644 --- a/src/corelib/thread/qthreadpool_p.h +++ b/src/corelib/thread/qthreadpool_p.h @@ -53,6 +53,7 @@ // #include "QtCore/qmutex.h" +#include "QtCore/qthread.h" #include "QtCore/qwaitcondition.h" #include "QtCore/qset.h" #include "QtCore/qqueue.h" @@ -91,11 +92,12 @@ public: QVector<QPair<QRunnable *, int> > queue; QWaitCondition noActiveThreads; - bool isExiting; - int expiryTimeout; - int maxThreadCount; - int reservedThreads; - int activeThreads; + int expiryTimeout = 30000; + int maxThreadCount = QThread::idealThreadCount(); + int reservedThreads = 0; + int activeThreads = 0; + uint stackSize = 0; + bool isExiting = false; }; QT_END_NAMESPACE diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 3ecb2ac662..9b648adb06 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -686,14 +686,6 @@ QByteArray qCompress(const uchar* data, int nbytes, int compressionLevel) \sa qCompress() */ -/*! \relates QByteArray - - \overload - - Uncompresses the first \a nbytes of \a data and returns a new byte - array with the uncompressed data. -*/ - #ifndef QT_NO_COMPRESS namespace { struct QByteArrayDataDeleter @@ -709,6 +701,13 @@ static QByteArray invalidCompressedData() return QByteArray(); } +/*! \relates QByteArray + + \overload + + Uncompresses the first \a nbytes of \a data and returns a new byte + array with the uncompressed data. +*/ QByteArray qUncompress(const uchar* data, int nbytes) { if (!data) { diff --git a/src/network/access/access.pri b/src/network/access/access.pri index 13d52ea44a..4281a870a7 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -41,7 +41,8 @@ HEADERS += \ access/qnetworkfile_p.h \ access/qhttp2protocolhandler_p.h \ access/qhsts_p.h \ - access/qhstspolicy.h + access/qhstspolicy.h \ + access/qhstsstore_p.h SOURCES += \ access/qftp.cpp \ @@ -76,7 +77,8 @@ SOURCES += \ access/qnetworkfile.cpp \ access/qhttp2protocolhandler.cpp \ access/qhsts.cpp \ - access/qhstspolicy.cpp + access/qhstspolicy.cpp \ + access/qhstsstore.cpp mac: LIBS_PRIVATE += -framework Security diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp index ca9f3b977b..6a731afc2f 100644 --- a/src/network/access/qhsts.cpp +++ b/src/network/access/qhsts.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ +#include "qhstsstore_p.h" #include "qhsts_p.h" #include "QtCore/private/qipaddress_p.h" @@ -80,14 +81,24 @@ void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &h return; QHstsHeaderParser parser; - if (parser.parse(headers)) + if (parser.parse(headers)) { updateKnownHost(url.host(), parser.expirationDate(), parser.includeSubDomains()); + if (hstsStore) + hstsStore->synchronize(); + } } void QHstsCache::updateFromPolicies(const QVector<QHstsPolicy> &policies) { for (const auto &policy : policies) updateKnownHost(policy.host(), policy.expiry(), policy.includesSubDomains()); + + if (hstsStore && policies.size()) { + // These policies are coming either from store or from QNAM's setter + // function. As a result we can notice expired or new policies, time + // to sync ... + hstsStore->synchronize(); + } } void QHstsCache::updateKnownHost(const QUrl &url, const QDateTime &expires, @@ -97,6 +108,8 @@ void QHstsCache::updateKnownHost(const QUrl &url, const QDateTime &expires, return; updateKnownHost(url.host(), expires, includeSubDomains); + if (hstsStore) + hstsStore->synchronize(); } void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires, @@ -124,13 +137,20 @@ void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires, } knownHosts.insert(pos, hostName, newPolicy); + if (hstsStore) + hstsStore->addToObserved(newPolicy); return; } if (newPolicy.isExpired()) knownHosts.erase(pos); - else + else if (*pos != newPolicy) *pos = std::move(newPolicy); + else + return; + + if (hstsStore) + hstsStore->addToObserved(newPolicy); } bool QHstsCache::isKnownHost(const QUrl &url) const @@ -165,10 +185,15 @@ bool QHstsCache::isKnownHost(const QUrl &url) const while (nameToTest.fragment.size()) { auto const pos = knownHosts.find(nameToTest); if (pos != knownHosts.end()) { - if (pos.value().isExpired()) + if (pos.value().isExpired()) { knownHosts.erase(pos); - else if (!superDomainMatch || pos.value().includesSubDomains()) + if (hstsStore) { + // Inform our store that this policy has expired. + hstsStore->addToObserved(pos.value()); + } + } else if (!superDomainMatch || pos.value().includesSubDomains()) { return true; + } } const int dot = nameToTest.fragment.indexOf(QLatin1Char('.')); @@ -196,6 +221,34 @@ QVector<QHstsPolicy> QHstsCache::policies() const return values; } +void QHstsCache::setStore(QHstsStore *store) +{ + // Caller retains ownership of store, which must outlive this cache. + if (store != hstsStore) { + hstsStore = store; + + if (!hstsStore) + return; + + // First we augment our store with the policies we already know about + // (and thus the cached policy takes priority over whatever policy we + // had in the store for the same host, if any). + if (knownHosts.size()) { + const QVector<QHstsPolicy> observed(policies()); + for (const auto &policy : observed) + hstsStore->addToObserved(policy); + hstsStore->synchronize(); + } + + // Now we update the cache with anything we have not observed yet, but + // the store knows about (well, it can happen we synchronize again as a + // result if some policies managed to expire or if we add a new one + // from the store to cache): + const QVector<QHstsPolicy> restored(store->readPolicies()); + updateFromPolicies(restored); + } +} + // The parser is quite simple: 'nextToken' knowns exactly what kind of tokens // are valid and it will return false if something else was found; then // we immediately stop parsing. 'parseDirective' knows how these tokens can diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h index ab3ca536fb..2feb73b446 100644 --- a/src/network/access/qhsts_p.h +++ b/src/network/access/qhsts_p.h @@ -51,6 +51,8 @@ // We mean it. // +#include <QtNetwork/private/qtnetworkglobal_p.h> + #include <QtNetwork/qhstspolicy.h> #include <QtCore/qbytearray.h> @@ -66,6 +68,8 @@ QT_BEGIN_NAMESPACE template<typename T> class QList; template <typename T> class QVector; +class QHstsStore; + class Q_AUTOTEST_EXPORT QHstsCache { public: @@ -80,6 +84,8 @@ public: QVector<QHstsPolicy> policies() const; + void setStore(QHstsStore *store); + private: void updateKnownHost(const QString &hostName, const QDateTime &expires, @@ -112,6 +118,7 @@ private: }; mutable QMap<HostName, QHstsPolicy> knownHosts; + QHstsStore *hstsStore = nullptr; }; class Q_AUTOTEST_EXPORT QHstsHeaderParser diff --git a/src/network/access/qhstsstore.cpp b/src/network/access/qhstsstore.cpp new file mode 100644 index 0000000000..239a52b7a4 --- /dev/null +++ b/src/network/access/qhstsstore.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhstsstore_p.h" +#include "qhstspolicy.h" + +#include "qstandardpaths.h" +#include "qdatastream.h" +#include "qbytearray.h" +#include "qdatetime.h" +#include "qvariant.h" +#include "qstring.h" +#include "qdir.h" + +#include <utility> + +QT_BEGIN_NAMESPACE + +static QString host_name_to_settings_key(const QString &hostName) +{ + const QByteArray hostNameAsHex(hostName.toUtf8().toHex()); + return QString::fromLatin1(hostNameAsHex); +} + +static QString settings_key_to_host_name(const QString &key) +{ + const QByteArray hostNameAsUtf8(QByteArray::fromHex(key.toLatin1())); + return QString::fromUtf8(hostNameAsUtf8); +} + +QHstsStore::QHstsStore(const QString &dirName) + : store(absoluteFilePath(dirName), QSettings::IniFormat) +{ + // Disable fallbacks, we do not want to use anything but our own ini file. + store.setFallbacksEnabled(false); +} + +QHstsStore::~QHstsStore() +{ + synchronize(); +} + +QVector<QHstsPolicy> QHstsStore::readPolicies() +{ + // This function only attempts to read policies, making no decision about + // expired policies. It's up to a user (QHstsCache) to mark these policies + // for deletion and sync the store later. But we immediately remove keys/values + // (if the store isWritable) for the policies that we fail to read. + QVector<QHstsPolicy> policies; + + beginHstsGroups(); + + const QStringList keys = store.childKeys(); + for (const auto &key : keys) { + QHstsPolicy restoredPolicy; + if (deserializePolicy(key, restoredPolicy)) { + restoredPolicy.setHost(settings_key_to_host_name(key)); + policies.push_back(std::move(restoredPolicy)); + } else if (isWritable()) { + evictPolicy(key); + } + } + + endHstsGroups(); + + return policies; +} + +void QHstsStore::addToObserved(const QHstsPolicy &policy) +{ + observedPolicies.push_back(policy); +} + +void QHstsStore::synchronize() +{ + if (!isWritable()) + return; + + if (observedPolicies.size()) { + beginHstsGroups(); + for (const QHstsPolicy &policy : observedPolicies) { + const QString key(host_name_to_settings_key(policy.host())); + // If we fail to write a new, updated policy, we also remove the old one. + if (policy.isExpired() || !serializePolicy(key, policy)) + evictPolicy(key); + } + observedPolicies.clear(); + endHstsGroups(); + } + + store.sync(); +} + +bool QHstsStore::isWritable() const +{ + return store.isWritable(); +} + +QString QHstsStore::absoluteFilePath(const QString &dirName) +{ + const QDir dir(dirName.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + : dirName); + return dir.absoluteFilePath(QLatin1String("hstsstore")); +} + +void QHstsStore::beginHstsGroups() +{ + store.beginGroup(QLatin1String("StrictTransportSecurity")); + store.beginGroup(QLatin1String("Policies")); +} + +void QHstsStore::endHstsGroups() +{ + store.endGroup(); + store.endGroup(); +} + +bool QHstsStore::deserializePolicy(const QString &key, QHstsPolicy &policy) +{ + Q_ASSERT(store.contains(key)); + + const QVariant data(store.value(key)); + if (data.isNull() || !data.canConvert<QByteArray>()) + return false; + + const QByteArray serializedData(data.toByteArray()); + QDataStream streamer(serializedData); + qint64 expiryInMS = 0; + streamer >> expiryInMS; + if (streamer.status() != QDataStream::Ok) + return false; + bool includesSubDomains = false; + streamer >> includesSubDomains; + if (streamer.status() != QDataStream::Ok) + return false; + + policy.setExpiry(QDateTime::fromMSecsSinceEpoch(expiryInMS)); + policy.setIncludesSubDomains(includesSubDomains); + + return true; +} + +bool QHstsStore::serializePolicy(const QString &key, const QHstsPolicy &policy) +{ + Q_ASSERT(store.isWritable()); + + QByteArray serializedData; + QDataStream streamer(&serializedData, QIODevice::WriteOnly); + streamer << policy.expiry().toMSecsSinceEpoch(); + streamer << policy.includesSubDomains(); + + if (streamer.status() != QDataStream::Ok) + return false; + + store.setValue(key, serializedData); + return true; +} + +void QHstsStore::evictPolicy(const QString &key) +{ + Q_ASSERT(store.isWritable()); + if (store.contains(key)) + store.remove(key); +} + +QT_END_NAMESPACE diff --git a/src/network/access/qhstsstore_p.h b/src/network/access/qhstsstore_p.h new file mode 100644 index 0000000000..13042839c4 --- /dev/null +++ b/src/network/access/qhstsstore_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHSTSSTORE_P_H +#define QHSTSSTORE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtNetwork/private/qtnetworkglobal_p.h> + +#include <QtCore/qsettings.h> +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +class QHstsPolicy; +class QByteArray; +class QString; + +class Q_AUTOTEST_EXPORT QHstsStore +{ +public: + explicit QHstsStore(const QString &dirName); + ~QHstsStore(); + + QVector<QHstsPolicy> readPolicies(); + void addToObserved(const QHstsPolicy &policy); + void synchronize(); + + bool isWritable() const; + + static QString absoluteFilePath(const QString &dirName); +private: + void beginHstsGroups(); + bool serializePolicy(const QString &key, const QHstsPolicy &policy); + bool deserializePolicy(const QString &key, QHstsPolicy &policy); + void evictPolicy(const QString &key); + void endHstsGroups(); + + QVector<QHstsPolicy> observedPolicies; + QSettings store; + + Q_DISABLE_COPY(QHstsStore) +}; + +QT_END_NAMESPACE + +#endif // QHSTSSTORE_P_H diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 79f0aa8038..eeee82a87c 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -730,6 +730,48 @@ bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const } /*! + \since 5.10 + + If \a enabled is \c true, the internal HSTS cache will use a persistent store + to read and write HSTS policies. \a storeDir defines where this store will be + located. The default location is defined by QStandardPaths::CacheLocation. + If there is no writable QStandartPaths::CacheLocation and \a storeDir is an + empty string, the store will be located in the program's working directory. + + \note If HSTS cache already contains HSTS policies by the time persistent + store is enabled, these policies will be preserved in the store. In case both + cache and store contain the same known hosts, policies from cache are considered + to be more up-to-date (and thus will overwrite the previous values in the store). + If this behavior is undesired, enable HSTS store before enabling Strict Tranport + Security. By default, the persistent store of HSTS policies is disabled. + + \sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(), + QStandardPaths::standardLocations() +*/ + +void QNetworkAccessManager::enableStrictTransportSecurityStore(bool enabled, const QString &storeDir) +{ + Q_D(QNetworkAccessManager); + d->stsStore.reset(enabled ? new QHstsStore(storeDir) : nullptr); + d->stsCache.setStore(d->stsStore.data()); +} + +/*! + \since 5.10 + + Returns true if HSTS cache uses a permanent store to load and store HSTS + policies. + + \sa enableStrictTransportSecurityStore() +*/ + +bool QNetworkAccessManager::isStrictTransportSecurityStoreEnabled() const +{ + Q_D(const QNetworkAccessManager); + return bool(d->stsStore.data()); +} + +/*! \since 5.9 Adds HTTP Strict Transport Security policies contained in \a knownHosts into HSTS cache. @@ -744,7 +786,7 @@ bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const policies, but this information can be overridden by "Strict-Transport-Security" response headers. - \sa addStrictTransportSecurityHosts(), QHstsPolicy + \sa addStrictTransportSecurityHosts(), enableStrictTransportSecurityStore(), QHstsPolicy */ void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts) diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index f035ac5b00..4806ec0475 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -42,6 +42,7 @@ #include <QtNetwork/qtnetworkglobal.h> #include <QtNetwork/qnetworkrequest.h> +#include <QtCore/QString> #include <QtCore/QVector> #include <QtCore/QObject> #ifndef QT_NO_SSL @@ -124,6 +125,8 @@ public: void setStrictTransportSecurityEnabled(bool enabled); bool isStrictTransportSecurityEnabled() const; + void enableStrictTransportSecurityStore(bool enabled, const QString &storeDir = QString()); + bool isStrictTransportSecurityStoreEnabled() const; void addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts); QVector<QHstsPolicy> strictTransportSecurityHosts() const; diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index 13a26a54f1..e5257251a4 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -56,6 +56,7 @@ #include "qnetworkaccesscache_p.h" #include "qnetworkaccessbackend_p.h" #include "qnetworkrequest.h" +#include "qhstsstore_p.h" #include "qhsts_p.h" #include "private/qobject_p.h" #include "QtNetwork/qnetworkproxy.h" @@ -211,6 +212,7 @@ public: Q_AUTOTEST_EXPORT static void clearConnectionCache(QNetworkAccessManager *manager); QHstsCache stsCache; + QScopedPointer<QHstsStore> stsStore; bool stsEnabled = false; #ifndef QT_NO_BEARERMANAGEMENT diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 15906961ac..3e445f0f7f 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -640,7 +640,7 @@ QWindowsWindowData // Capture events before CreateWindowEx() returns. The context is cleared in // the QWindowsWindow constructor. - const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, data.customMargins, style, exStyle)); + const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, rect, data.customMargins, style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); qCDebug(lcQpaWindows).nospace() @@ -1004,10 +1004,11 @@ void QWindowsForeignWindow::setVisible(bool visible) */ QWindowCreationContext::QWindowCreationContext(const QWindow *w, - const QRect &geometry, + const QRect &geometryIn, const QRect &geometry, const QMargins &cm, DWORD style_, DWORD exStyle_) : geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_), + requestedGeometryIn(geometryIn), requestedGeometry(geometry), obtainedGeometry(geometry), margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm) { @@ -1124,7 +1125,7 @@ void QWindowsWindow::initialize() if (w->type() != Qt::Desktop) { const Qt::WindowState state = w->windowState(); if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen - && creationContext->requestedGeometry != creationContext->obtainedGeometry) { + && creationContext->requestedGeometryIn != creationContext->obtainedGeometry) { QWindowSystemInterface::handleGeometryChange(w, creationContext->obtainedGeometry); } QPlatformScreen *obtainedScreen = screenForGeometry(creationContext->obtainedGeometry); diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 982f3dfd30..f0789e5167 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -81,9 +81,10 @@ struct QWindowsGeometryHint struct QWindowCreationContext { - QWindowCreationContext(const QWindow *w, const QRect &r, - const QMargins &customMargins, - DWORD style, DWORD exStyle); + explicit QWindowCreationContext(const QWindow *w, + const QRect &geometryIn, const QRect &geometry, + const QMargins &customMargins, + DWORD style, DWORD exStyle); void applyToMinMaxInfo(MINMAXINFO *mmi) const { geometryHint.applyToMinMaxInfo(style, exStyle, mmi); } @@ -91,7 +92,8 @@ struct QWindowCreationContext const QWindow *window; DWORD style; DWORD exStyle; - QRect requestedGeometry; + QRect requestedGeometryIn; // QWindow scaled + QRect requestedGeometry; // after QPlatformWindow::initialGeometry() QRect obtainedGeometry; QMargins margins; QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE |