blob: 8eba9ccc4242be6247d4d991f1e053b0992e3d19 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins 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$
**
****************************************************************************/
// see comment in ../platformdefs_win.h.
#define WIN32_LEAN_AND_MEAN 1
#include "qnetworksession_impl.h"
#include "qbearerengine_impl.h"
#include <QtNetwork/qnetworksession.h>
#include <QtNetwork/private/qnetworkconfigmanager_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
static QBearerEngineImpl *getEngineFromId(const QString &id)
{
QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
if (priv) {
const auto engines = priv->engines();
for (QBearerEngine *engine : engines) {
QBearerEngineImpl *engineImpl = qobject_cast<QBearerEngineImpl *>(engine);
if (engineImpl && engineImpl->hasIdentifier(id))
return engineImpl;
}
}
return nullptr;
}
class QNetworkSessionManagerPrivate : public QObject
{
Q_OBJECT
public:
QNetworkSessionManagerPrivate(QObject *parent = nullptr) : QObject(parent) {}
~QNetworkSessionManagerPrivate() {}
inline void forceSessionClose(const QNetworkConfiguration &config)
{ emit forcedSessionClose(config); }
Q_SIGNALS:
void forcedSessionClose(const QNetworkConfiguration &config);
};
Q_GLOBAL_STATIC(QNetworkSessionManagerPrivate, sessionManager);
void QNetworkSessionPrivateImpl::syncStateWithInterface()
{
connect(sessionManager(), SIGNAL(forcedSessionClose(QNetworkConfiguration)),
this, SLOT(forcedSessionClose(QNetworkConfiguration)));
opened = false;
isOpen = false;
state = QNetworkSession::Invalid;
lastError = QNetworkSession::UnknownSessionError;
qRegisterMetaType<QBearerEngineImpl::ConnectionError>();
switch (publicConfig.type()) {
case QNetworkConfiguration::InternetAccessPoint:
activeConfig = publicConfig;
engine = getEngineFromId(activeConfig.identifier());
if (engine) {
qRegisterMetaType<QNetworkConfigurationPrivatePointer>();
connect(engine, SIGNAL(configurationChanged(QNetworkConfigurationPrivatePointer)),
this, SLOT(configurationChanged(QNetworkConfigurationPrivatePointer)),
Qt::QueuedConnection);
connect(engine, SIGNAL(connectionError(QString,QBearerEngineImpl::ConnectionError)),
this, SLOT(connectionError(QString,QBearerEngineImpl::ConnectionError)),
Qt::QueuedConnection);
}
break;
case QNetworkConfiguration::ServiceNetwork:
serviceConfig = publicConfig;
// Defer setting engine and signals until open().
Q_FALLTHROUGH();
case QNetworkConfiguration::UserChoice:
// Defer setting serviceConfig and activeConfig until open().
Q_FALLTHROUGH();
default:
engine = nullptr;
}
networkConfigurationsChanged();
}
void QNetworkSessionPrivateImpl::open()
{
if (serviceConfig.isValid()) {
lastError = QNetworkSession::OperationNotSupportedError;
emit QNetworkSessionPrivate::error(lastError);
} else if (!isOpen) {
if ((activeConfig.state() & QNetworkConfiguration::Discovered) != QNetworkConfiguration::Discovered) {
lastError = QNetworkSession::InvalidConfigurationError;
state = QNetworkSession::Invalid;
emit stateChanged(state);
emit QNetworkSessionPrivate::error(lastError);
return;
}
opened = true;
if ((activeConfig.state() & QNetworkConfiguration::Active) != QNetworkConfiguration::Active &&
(activeConfig.state() & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) {
state = QNetworkSession::Connecting;
emit stateChanged(state);
engine->connectToId(activeConfig.identifier());
}
isOpen = (activeConfig.state() & QNetworkConfiguration::Active) == QNetworkConfiguration::Active;
if (isOpen)
emit quitPendingWaitsForOpened();
}
}
void QNetworkSessionPrivateImpl::close()
{
if (serviceConfig.isValid()) {
lastError = QNetworkSession::OperationNotSupportedError;
emit QNetworkSessionPrivate::error(lastError);
} else if (isOpen) {
opened = false;
isOpen = false;
emit closed();
}
}
void QNetworkSessionPrivateImpl::stop()
{
if (serviceConfig.isValid()) {
lastError = QNetworkSession::OperationNotSupportedError;
emit QNetworkSessionPrivate::error(lastError);
} else {
if ((activeConfig.state() & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
state = QNetworkSession::Closing;
emit stateChanged(state);
engine->disconnectFromId(activeConfig.identifier());
sessionManager()->forceSessionClose(activeConfig);
}
opened = false;
isOpen = false;
emit closed();
}
}
void QNetworkSessionPrivateImpl::migrate()
{
}
void QNetworkSessionPrivateImpl::accept()
{
}
void QNetworkSessionPrivateImpl::ignore()
{
}
void QNetworkSessionPrivateImpl::reject()
{
}
#ifndef QT_NO_NETWORKINTERFACE
QNetworkInterface QNetworkSessionPrivateImpl::currentInterface() const
{
if (!engine || state != QNetworkSession::Connected || !publicConfig.isValid())
return QNetworkInterface();
QString iface = engine->getInterfaceFromId(activeConfig.identifier());
if (iface.isEmpty())
return QNetworkInterface();
return QNetworkInterface::interfaceFromName(iface);
}
#endif
QVariant QNetworkSessionPrivateImpl::sessionProperty(const QString &key) const
{
if (key == QLatin1String("AutoCloseSessionTimeout")) {
if (engine && engine->requiresPolling() &&
!(engine->capabilities() & QNetworkConfigurationManager::CanStartAndStopInterfaces)) {
return sessionTimeout >= 0 ? sessionTimeout * 10000 : -1;
}
}
return QVariant();
}
void QNetworkSessionPrivateImpl::setSessionProperty(const QString &key, const QVariant &value)
{
if (key == QLatin1String("AutoCloseSessionTimeout")) {
if (engine && engine->requiresPolling() &&
!(engine->capabilities() & QNetworkConfigurationManager::CanStartAndStopInterfaces)) {
int timeout = value.toInt();
if (timeout >= 0) {
connect(engine, SIGNAL(updateCompleted()),
this, SLOT(decrementTimeout()), Qt::UniqueConnection);
sessionTimeout = timeout / 10000; // convert to poll intervals
} else {
disconnect(engine, SIGNAL(updateCompleted()), this, SLOT(decrementTimeout()));
sessionTimeout = -1;
}
}
}
}
QString QNetworkSessionPrivateImpl::errorString() const
{
switch (lastError) {
case QNetworkSession::UnknownSessionError:
return tr("Unknown session error.");
case QNetworkSession::SessionAbortedError:
return tr("The session was aborted by the user or system.");
case QNetworkSession::OperationNotSupportedError:
return tr("The requested operation is not supported by the system.");
case QNetworkSession::InvalidConfigurationError:
return tr("The specified configuration cannot be used.");
case QNetworkSession::RoamingError:
return tr("Roaming was aborted or is not possible.");
default:
break;
}
return QString();
}
QNetworkSession::SessionError QNetworkSessionPrivateImpl::error() const
{
return lastError;
}
quint64 QNetworkSessionPrivateImpl::bytesWritten() const
{
if (engine && state == QNetworkSession::Connected)
return engine->bytesWritten(activeConfig.identifier());
return Q_UINT64_C(0);
}
quint64 QNetworkSessionPrivateImpl::bytesReceived() const
{
if (engine && state == QNetworkSession::Connected)
return engine->bytesReceived(activeConfig.identifier());
return Q_UINT64_C(0);
}
quint64 QNetworkSessionPrivateImpl::activeTime() const
{
if (state == QNetworkSession::Connected && startTime != Q_UINT64_C(0))
return QDateTime::currentSecsSinceEpoch() - startTime;
return Q_UINT64_C(0);
}
QNetworkSession::UsagePolicies QNetworkSessionPrivateImpl::usagePolicies() const
{
return currentPolicies;
}
void QNetworkSessionPrivateImpl::setUsagePolicies(QNetworkSession::UsagePolicies newPolicies)
{
if (newPolicies != currentPolicies) {
currentPolicies = newPolicies;
emit usagePoliciesChanged(currentPolicies);
}
}
void QNetworkSessionPrivateImpl::updateStateFromServiceNetwork()
{
QNetworkSession::State oldState = state;
const auto configs = serviceConfig.children();
for (const QNetworkConfiguration &config : configs) {
if ((config.state() & QNetworkConfiguration::Active) != QNetworkConfiguration::Active)
continue;
if (activeConfig != config) {
if (engine) {
disconnect(engine, SIGNAL(connectionError(QString,QBearerEngineImpl::ConnectionError)),
this, SLOT(connectionError(QString,QBearerEngineImpl::ConnectionError)));
}
activeConfig = config;
engine = getEngineFromId(activeConfig.identifier());
if (engine) {
connect(engine, SIGNAL(connectionError(QString,QBearerEngineImpl::ConnectionError)),
this, SLOT(connectionError(QString,QBearerEngineImpl::ConnectionError)),
Qt::QueuedConnection);
}
emit newConfigurationActivated();
}
state = QNetworkSession::Connected;
if (state != oldState)
emit stateChanged(state);
return;
}
if (serviceConfig.children().isEmpty())
state = QNetworkSession::NotAvailable;
else
state = QNetworkSession::Disconnected;
if (state != oldState)
emit stateChanged(state);
}
void QNetworkSessionPrivateImpl::updateStateFromActiveConfig()
{
if (!engine)
return;
QNetworkSession::State oldState = state;
state = engine->sessionStateForId(activeConfig.identifier());
bool oldActive = isOpen;
isOpen = (state == QNetworkSession::Connected) ? opened : false;
if (!oldActive && isOpen)
emit quitPendingWaitsForOpened();
if (oldActive && !isOpen)
emit closed();
if (oldState != state)
emit stateChanged(state);
}
void QNetworkSessionPrivateImpl::networkConfigurationsChanged()
{
if (serviceConfig.isValid())
updateStateFromServiceNetwork();
else
updateStateFromActiveConfig();
if (engine)
startTime = engine->startTime(activeConfig.identifier());
}
void QNetworkSessionPrivateImpl::configurationChanged(QNetworkConfigurationPrivatePointer config)
{
if (serviceConfig.isValid() &&
(config->id == serviceConfig.identifier() || config->id == activeConfig.identifier())) {
updateStateFromServiceNetwork();
} else if (config->id == activeConfig.identifier()) {
updateStateFromActiveConfig();
}
}
void QNetworkSessionPrivateImpl::forcedSessionClose(const QNetworkConfiguration &config)
{
if (activeConfig == config) {
opened = false;
isOpen = false;
emit closed();
lastError = QNetworkSession::SessionAbortedError;
emit QNetworkSessionPrivate::error(lastError);
}
}
void QNetworkSessionPrivateImpl::connectionError(const QString &id, QBearerEngineImpl::ConnectionError error)
{
if (activeConfig.identifier() == id) {
networkConfigurationsChanged();
switch (error) {
case QBearerEngineImpl::OperationNotSupported:
lastError = QNetworkSession::OperationNotSupportedError;
opened = false;
break;
case QBearerEngineImpl::InterfaceLookupError:
case QBearerEngineImpl::ConnectError:
case QBearerEngineImpl::DisconnectionError:
default:
lastError = QNetworkSession::UnknownSessionError;
}
emit QNetworkSessionPrivate::error(lastError);
}
}
void QNetworkSessionPrivateImpl::decrementTimeout()
{
if (--sessionTimeout <= 0) {
disconnect(engine, SIGNAL(updateCompleted()), this, SLOT(decrementTimeout()));
sessionTimeout = -1;
close();
}
}
QT_END_NAMESPACE
#include "qnetworksession_impl.moc"