diff options
author | David Boddie <dboddie@trolltech.com> | 2010-03-30 14:56:41 (GMT) |
---|---|---|
committer | David Boddie <dboddie@trolltech.com> | 2010-03-30 14:56:41 (GMT) |
commit | 87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3 (patch) | |
tree | f8b8c24056d54e19937dea1b0301af558597726a /src/network | |
parent | 09ce407aaa4a00013a606bf0011faf6cbc654c72 (diff) | |
parent | 00f7426f3906361fb5addb36e428648eee5e2983 (diff) | |
download | Qt-87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3.zip Qt-87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3.tar.gz Qt-87f66d52e362de003a9f2cdaafbfbe3ba4e5fbc3.tar.bz2 |
Merge branch '4.7' of git@scm.dev.nokia.troll.no:qt/oslo-staging-2 into 4.7
Conflicts:
doc/src/modules.qdoc
mkspecs/common/symbian/symbian.conf
src/gui/graphicsview/qgraphicswidget.h
src/s60installs/bwins/QtGuiu.def
src/s60installs/eabi/QtGuiu.def
Diffstat (limited to 'src/network')
46 files changed, 4007 insertions, 125 deletions
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp index 8cdfe6a..9eb2399 100644 --- a/src/network/access/qhttpnetworkrequest.cpp +++ b/src/network/access/qhttpnetworkrequest.cpp @@ -61,6 +61,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest uploadByteDevice = other.uploadByteDevice; autoDecompress = other.autoDecompress; pipeliningAllowed = other.pipeliningAllowed; + customVerb = other.customVerb; } QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate() @@ -76,36 +77,38 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot QByteArray QHttpNetworkRequestPrivate::methodName() const { - QByteArray ba; switch (operation) { - case QHttpNetworkRequest::Options: - ba += "OPTIONS"; - break; case QHttpNetworkRequest::Get: - ba += "GET"; + return "GET"; break; case QHttpNetworkRequest::Head: - ba += "HEAD"; + return "HEAD"; break; case QHttpNetworkRequest::Post: - ba += "POST"; + return "POST"; + break; + case QHttpNetworkRequest::Options: + return "OPTIONS"; break; case QHttpNetworkRequest::Put: - ba += "PUT"; + return "PUT"; break; case QHttpNetworkRequest::Delete: - ba += "DELETE"; + return "DELETE"; break; case QHttpNetworkRequest::Trace: - ba += "TRACE"; + return "TRACE"; break; case QHttpNetworkRequest::Connect: - ba += "CONNECT"; + return "CONNECT"; + break; + case QHttpNetworkRequest::Custom: + return customVerb; break; default: break; } - return ba; + return QByteArray(); } QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const @@ -128,26 +131,37 @@ QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy) { - QByteArray ba = request.d->methodName(); - QByteArray uri = request.d->uri(throughProxy); - ba += ' ' + uri; + QList<QPair<QByteArray, QByteArray> > fields = request.header(); + QByteArray ba; + ba.reserve(40 + fields.length()*25); // very rough lower bound estimation - QString majorVersion = QString::number(request.majorVersion()); - QString minorVersion = QString::number(request.minorVersion()); - ba += " HTTP/" + majorVersion.toLatin1() + '.' + minorVersion.toLatin1() + "\r\n"; + ba += request.d->methodName(); + ba += ' '; + ba += request.d->uri(throughProxy); + + ba += " HTTP/"; + ba += QByteArray::number(request.majorVersion()); + ba += '.'; + ba += QByteArray::number(request.minorVersion()); + ba += "\r\n"; - QList<QPair<QByteArray, QByteArray> > fields = request.header(); QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin(); - for (; it != fields.constEnd(); ++it) - ba += it->first + ": " + it->second + "\r\n"; + QList<QPair<QByteArray, QByteArray> >::const_iterator endIt = fields.constEnd(); + for (; it != endIt; ++it) { + ba += it->first; + ba += ": "; + ba += it->second; + ba += "\r\n"; + } if (request.d->operation == QHttpNetworkRequest::Post) { // add content type, if not set in the request if (request.headerField("content-type").isEmpty()) ba += "Content-Type: application/x-www-form-urlencoded\r\n"; if (!request.d->uploadByteDevice && request.d->url.hasQuery()) { QByteArray query = request.d->url.encodedQuery(); - ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n"; - ba += "\r\n"; + ba += "Content-Length: "; + ba += QByteArray::number(query.size()); + ba += "\r\n\r\n"; ba += query; } else { ba += "\r\n"; @@ -230,6 +244,16 @@ void QHttpNetworkRequest::setOperation(Operation operation) d->operation = operation; } +QByteArray QHttpNetworkRequest::customVerb() const +{ + return d->customVerb; +} + +void QHttpNetworkRequest::setCustomVerb(const QByteArray &customVerb) +{ + d->customVerb = customVerb; +} + QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const { return d->priority; diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h index dad118e..1b35a84 100644 --- a/src/network/access/qhttpnetworkrequest_p.h +++ b/src/network/access/qhttpnetworkrequest_p.h @@ -72,7 +72,8 @@ public: Put, Delete, Trace, - Connect + Connect, + Custom }; enum Priority { @@ -103,6 +104,9 @@ public: Operation operation() const; void setOperation(Operation operation); + QByteArray customVerb() const; + void setCustomVerb(const QByteArray &customOperation); + Priority priority() const; void setPriority(Priority priority); @@ -133,6 +137,7 @@ public: static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy); QHttpNetworkRequest::Operation operation; + QByteArray customVerb; QHttpNetworkRequest::Priority priority; mutable QNonContiguousByteDevice* uploadByteDevice; bool autoDecompress; diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 8ac64d2..1fcfebb 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -46,9 +46,11 @@ #include "qnetworkreply_p.h" #include "QtCore/qhash.h" #include "QtCore/qmutex.h" +#include "QtNetwork/qnetworksession.h" #include "qnetworkaccesscachebackend_p.h" #include "qabstractnetworkcache.h" +#include "qhostinfo.h" #include "private/qnoncontiguousbytedevice_p.h" @@ -120,8 +122,11 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() if (reply->outgoingDataBuffer) device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer); - else + else if (reply->outgoingData) { device = QNonContiguousByteDeviceFactory::create(reply->outgoingData); + } else { + return 0; + } bool bufferDisallowed = reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, @@ -341,4 +346,35 @@ void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors) #endif } +/*! + Starts the backend. Returns true if the backend is started. Returns false if the backend + could not be started due to an unopened or roaming session. The caller should recall this + function once the session has been opened or the roaming process has finished. +*/ +bool QNetworkAccessBackend::start() +{ + if (!manager->networkSession) { + open(); + return true; + } + + // This is not ideal. + const QString host = reply->url.host(); + if (host == QLatin1String("localhost") || + QHostAddress(host) == QHostAddress::LocalHost || + QHostAddress(host) == QHostAddress::LocalHostIPv6) { + // Don't need an open session for localhost access. + open(); + return true; + } + + if (manager->networkSession->isOpen() && + manager->networkSession->state() == QNetworkSession::Connected) { + open(); + return true; + } + + return false; +} + QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 43d993c..9bc15e5 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -111,6 +111,7 @@ public: // socket). virtual void open() = 0; + virtual bool start(); virtual void closeDownstreamChannel() = 0; virtual bool waitForDownstreamReadyRead(int msecs) = 0; @@ -160,6 +161,10 @@ public: // This will possibly enable buffering of the upload data. virtual bool needsResetableUploadData() { return false; } + // Returns true if backend is able to resume downloads. + virtual bool canResume() const { return false; } + virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); } + protected: // Create the device used for reading the upload data QNonContiguousByteDevice* createUploadByteDevice(); @@ -190,6 +195,7 @@ private: friend class QNetworkAccessManager; friend class QNetworkAccessManagerPrivate; friend class QNetworkAccessBackendUploadIODevice; + friend class QNetworkReplyImplPrivate; QNetworkAccessManagerPrivate *manager; QNetworkReplyImplPrivate *reply; }; diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp index 52c554f..efb6e3e 100644 --- a/src/network/access/qnetworkaccessdatabackend.cpp +++ b/src/network/access/qnetworkaccessdatabackend.cpp @@ -43,6 +43,8 @@ #include "qnetworkrequest.h" #include "qnetworkreply.h" #include "qurlinfo.h" +#include "private/qdataurl_p.h" +#include <qcoreapplication.h> QT_BEGIN_NAMESPACE @@ -71,64 +73,33 @@ void QNetworkAccessDataBackend::open() if (operation() != QNetworkAccessManager::GetOperation && operation() != QNetworkAccessManager::HeadOperation) { // data: doesn't support anything but GET - QString msg = QObject::tr("Operation not supported on %1") + const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend", + "Operation not supported on %1") .arg(uri.toString()); error(QNetworkReply::ContentOperationNotPermittedError, msg); finished(); return; } - if (uri.host().isEmpty()) { - setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/plain;charset=US-ASCII")); - - // the following would have been the correct thing, but - // reality often differs from the specification. People have - // data: URIs with ? and # - //QByteArray data = QByteArray::fromPercentEncoding(uri.encodedPath()); - QByteArray data = QByteArray::fromPercentEncoding(uri.toEncoded()); - - // remove the data: scheme - data.remove(0, 5); - - // parse it: - int pos = data.indexOf(','); - if (pos != -1) { - QByteArray payload = data.mid(pos + 1); - data.truncate(pos); - data = data.trimmed(); - - // find out if the payload is encoded in Base64 - if (data.endsWith(";base64")) { - payload = QByteArray::fromBase64(payload); - data.chop(7); - } - - if (data.toLower().startsWith("charset")) { - int i = 7; // strlen("charset") - while (data.at(i) == ' ') - ++i; - if (data.at(i) == '=') - data.prepend("text/plain;"); - } - - if (!data.isEmpty()) - setHeader(QNetworkRequest::ContentTypeHeader, data.trimmed()); - - setHeader(QNetworkRequest::ContentLengthHeader, payload.size()); - emit metaDataChanged(); - - QByteDataBuffer list; - list.append(payload); - payload.clear(); // important because of implicit sharing! - writeDownstreamData(list); - - finished(); - return; - } + QPair<QString, QByteArray> decoded = qDecodeDataUrl(uri); + + if (! decoded.first.isNull()) { + setHeader(QNetworkRequest::ContentTypeHeader, decoded.first); + setHeader(QNetworkRequest::ContentLengthHeader, decoded.second.size()); + emit metaDataChanged(); + + QByteDataBuffer list; + list.append(decoded.second); + decoded.second.clear(); // important because of implicit sharing! + writeDownstreamData(list); + + finished(); + return; } // something wrong with this URI - QString msg = QObject::tr("Invalid URI: %1").arg(uri.toString()); + const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend", + "Invalid URI: %1").arg(uri.toString()); error(QNetworkReply::ProtocolFailure, msg); finished(); } diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp index 5926d0b..cd077e7 100644 --- a/src/network/access/qnetworkaccessdebugpipebackend.cpp +++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp @@ -252,7 +252,7 @@ void QNetworkAccessDebugPipeBackend::socketError() break; } - error(code, QObject::tr("Socket error on %1: %2") + error(code, QNetworkAccessDebugPipeBackend::tr("Socket error on %1: %2") .arg(url().toString(), socket.errorString())); finished(); disconnect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); @@ -267,7 +267,7 @@ void QNetworkAccessDebugPipeBackend::socketDisconnected() // normal close } else { // abnormal close - QString msg = QObject::tr("Remote host closed the connection prematurely on %1") + QString msg = QNetworkAccessDebugPipeBackend::tr("Remote host closed the connection prematurely on %1") .arg(url().toString()); error(QNetworkReply::RemoteHostClosedError, msg); finished(); diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index af971a7..7a48c2b 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -213,6 +213,7 @@ QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op, case QNetworkAccessManager::HeadOperation: case QNetworkAccessManager::PutOperation: case QNetworkAccessManager::DeleteOperation: + case QNetworkAccessManager::CustomOperation: break; default: @@ -296,6 +297,7 @@ QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() #ifndef QT_NO_OPENSSL , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false) #endif + , resumeOffset(0) { } @@ -480,10 +482,24 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, loadedFromCache = false; } +static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio) +{ + switch (prio) { + case QNetworkRequest::LowPriority: + return QHttpNetworkRequest::LowPriority; + case QNetworkRequest::HighPriority: + return QHttpNetworkRequest::HighPriority; + case QNetworkRequest::NormalPriority: + default: + return QHttpNetworkRequest::NormalPriority; + } +} + void QNetworkAccessHttpBackend::postRequest() { bool loadedFromCache = false; QHttpNetworkRequest httpRequest; + httpRequest.setPriority(convert(request().priority())); switch (operation()) { case QNetworkAccessManager::GetOperation: httpRequest.setOperation(QHttpNetworkRequest::Get); @@ -512,6 +528,14 @@ void QNetworkAccessHttpBackend::postRequest() httpRequest.setOperation(QHttpNetworkRequest::Delete); break; + case QNetworkAccessManager::CustomOperation: + invalidateCache(); // for safety reasons, we don't know what the operation does + httpRequest.setOperation(QHttpNetworkRequest::Custom); + httpRequest.setUploadByteDevice(createUploadByteDevice()); + httpRequest.setCustomVerb(request().attribute( + QNetworkRequest::CustomVerbAttribute).toByteArray()); + break; + default: break; // can't happen } @@ -519,6 +543,28 @@ void QNetworkAccessHttpBackend::postRequest() httpRequest.setUrl(url()); QList<QByteArray> headers = request().rawHeaderList(); + if (resumeOffset != 0) { + if (headers.contains("Range")) { + // Need to adjust resume offset for user specified range + + headers.removeOne("Range"); + + // We've already verified that requestRange starts with "bytes=", see canResume. + QByteArray requestRange = request().rawHeader("Range").mid(6); + + int index = requestRange.indexOf('-'); + + quint64 requestStartOffset = requestRange.left(index).toULongLong(); + quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong(); + + requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) + + '-' + QByteArray::number(requestEndOffset); + + httpRequest.setHeaderField("Range", requestRange); + } else { + httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-'); + } + } foreach (const QByteArray &header, headers) httpRequest.setHeaderField(header, request().rawHeader(header)); @@ -1094,6 +1140,31 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo return metaData; } +bool QNetworkAccessHttpBackend::canResume() const +{ + // Only GET operation supports resuming. + if (operation() != QNetworkAccessManager::GetOperation) + return false; + + // Can only resume if server/resource supports Range header. + if (httpReply->headerField("Accept-Ranges", "none") == "none") + return false; + + // We only support resuming for byte ranges. + if (request().hasRawHeader("Range")) { + QByteArray range = request().rawHeader("Range"); + if (!range.startsWith("bytes=")) + return false; + } + + return true; +} + +void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset) +{ + resumeOffset = offset; +} + QT_END_NAMESPACE #endif // QT_NO_HTTP diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 0eaf003..e5cc0ab 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -99,6 +99,9 @@ public: // we return true since HTTP needs to send PUT/POST data again after having authenticated bool needsResetableUploadData() { return true; } + bool canResume() const; + void setResumeOffset(quint64 offset); + private slots: void replyReadyRead(); void replyFinished(); @@ -120,6 +123,8 @@ private: QList<QSslError> pendingIgnoreSslErrorsList; #endif + quint64 resumeOffset; + void disconnectFromHttp(); void setupConnection(); void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 03b7192..197d89e 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -47,6 +47,8 @@ #include "qnetworkcookie.h" #include "qabstractnetworkcache.h" +#include "QtNetwork/qnetworksession.h" + #include "qnetworkaccesshttpbackend_p.h" #include "qnetworkaccessftpbackend_p.h" #include "qnetworkaccessfilebackend_p.h" @@ -59,6 +61,7 @@ #include "QtCore/qvector.h" #include "QtNetwork/qauthenticator.h" #include "QtNetwork/qsslconfiguration.h" +#include "QtNetwork/qnetworkconfigmanager.h" QT_BEGIN_NAMESPACE @@ -161,12 +164,70 @@ static void ensureInitialized() \value DeleteOperation delete contents operation (created with deleteResource()) + \value CustomOperation custom operation (created with + sendCustomRequest()) + \omitvalue UnknownOperation \sa QNetworkReply::operation() */ /*! + \enum QNetworkAccessManager::NetworkAccessibility + + Indicates whether the network is accessible via this network access manager. + + \value UnknownAccessibility The network accessibility cannot be determined. + \value NotAccessible The network is not currently accessible, either because there + is currently no network coverage or network access has been + explicitly disabled by a call to setNetworkAccessible(). + \value Accessible The network is accessible. + + \sa networkAccessible +*/ + +/*! + \property QNetworkAccessManager::networkAccessible + \brief whether the network is currently accessible via this network access manager. + + \since 4.7 + + If the network is \l {NotAccessible}{not accessible} the network access manager will not + process any new network requests, all such requests will fail with an error. Requests with + URLs with the file:// scheme will still be processed. + + By default the value of this property reflects the physical state of the device. Applications + may override it to disable all network requests via this network access manager by calling + + \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 4 + + Network requests can be reenabled again by calling + + \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 5 + + \note Calling setNetworkAccessible() does not change the network state. +*/ + +/*! + \fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) + + This signal is emitted when the value of the \l networkAccessible property changes. + \a accessible is the new network accessibility. +*/ + +/*! + \fn void QNetworkAccessManager::networkSessionConnected() + + \since 4.7 + + \internal + + This signal is emitted when the status of the network session changes into a usable (Connected) + state. It is used to signal to QNetworkReplys to start or migrate their network operation once + the network session has been opened or finished roaming. +*/ + +/*! \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) This signal is emitted whenever a proxy requests authentication @@ -573,7 +634,7 @@ QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request) The contents as well as associated headers will be downloaded. - \sa post(), put(), deleteResource() + \sa post(), put(), deleteResource(), sendCustomRequest() */ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) { @@ -592,7 +653,7 @@ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) \note Sending a POST request on protocols other than HTTP and HTTPS is undefined and will probably fail. - \sa get(), put(), deleteResource() + \sa get(), put(), deleteResource(), sendCustomRequest() */ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data) { @@ -633,7 +694,7 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const do not allow. Form upload mechanisms, including that of uploading files through HTML forms, use the POST mechanism. - \sa get(), post() + \sa get(), post(), deleteResource(), sendCustomRequest() */ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data) { @@ -664,7 +725,7 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const \note This feature is currently available for HTTP only, performing an HTTP DELETE request. - \sa get(), post(), put() + \sa get(), post(), put(), sendCustomRequest() */ QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request) { @@ -672,6 +733,148 @@ QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &requ } /*! + \since 4.7 + + Sets the network configuration that will be used when creating the + \l {QNetworkSession}{network session} to \a config. + + The network configuration is used to create and open a network session before any request that + requires network access is process. If no network configuration is explicitly set via this + function the network configuration returned by + QNetworkConfigurationManager::defaultConfiguration() will be used. + + To restore the default network configuration set the network configuration to the value + returned from QNetworkConfigurationManager::defaultConfiguration(). + + \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 2 + + If an invalid network configuration is set, a network session will not be created. In this + case network requests will be processed regardless, but may fail. For example: + + \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 3 + + \sa configuration(), QNetworkSession +*/ +void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config) +{ + d_func()->createSession(config); +} + +/*! + \since 4.7 + + Returns the network configuration that will be used to create the + \l {QNetworkSession}{network session} which will be used when processing network requests. + + \sa setConfiguration(), activeConfiguration() +*/ +QNetworkConfiguration QNetworkAccessManager::configuration() const +{ + Q_D(const QNetworkAccessManager); + + if (d->networkSession) + return d->networkSession->configuration(); + else + return QNetworkConfiguration(); +} + +/*! + \since 4.7 + + Returns the current active network configuration. + + If the network configuration returned by configuration() is of type + QNetworkConfiguration::ServiceNetwork this function will return the current active child + network configuration of that configuration. Otherwise returns the same network configuration + as configuration(). + + Use this function to return the actual network configuration currently in use by the network + session. + + \sa configuration() +*/ +QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const +{ + Q_D(const QNetworkAccessManager); + + if (d->networkSession) { + QNetworkConfigurationManager manager; + + return manager.configurationFromIdentifier( + d->networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString()); + } else { + return QNetworkConfiguration(); + } +} + +/*! + \since 4.7 + + Overrides the reported network accessibility. If \a accessible is NotAccessible the reported + network accessiblity will always be NotAccessible. Otherwise the reported network + accessibility will reflect the actual device state. +*/ +void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible) +{ + Q_D(QNetworkAccessManager); + + if (d->networkAccessible != accessible) { + NetworkAccessibility previous = networkAccessible(); + d->networkAccessible = accessible; + NetworkAccessibility current = networkAccessible(); + if (previous != current) + emit networkAccessibleChanged(current); + } +} + +/*! + \since 4.7 + + Returns the current network accessibility. +*/ +QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const +{ + Q_D(const QNetworkAccessManager); + + if (d->networkSession) { + // d->online holds online/offline state of this network session. + if (d->online) + return d->networkAccessible; + else + return NotAccessible; + } else { + // Network accessibility is either disabled or unknown. + return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility; + } +} + +/*! + \since 4.7 + + Sends a custom request to the server identified by the URL of \a request. + + It is the user's responsibility to send a \a verb to the server that is valid + according to the HTTP specification. + + This method provides means to send verbs other than the common ones provided + via get() or post() etc., for instance sending an HTTP OPTIONS command. + + If \a data is not empty, the contents of the \a data + device will be uploaded to the server; in that case, data must be open for + reading and must remain valid until the finished() signal is emitted for this reply. + + \note This feature is currently available for HTTP only. + + \sa get(), post(), put(), deleteResource() +*/ +QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data) +{ + QNetworkRequest newRequest(request); + newRequest.setAttribute(QNetworkRequest::CustomVerbAttribute, verb); + return d_func()->postProcess(createRequest(QNetworkAccessManager::CustomOperation, newRequest, data)); +} + +/*! Returns a new QNetworkReply object to handle the operation \a op and request \a req. The device \a outgoingData is always 0 for Get and Head requests, but is the value passed to post() and put() in @@ -700,6 +903,28 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera return new QFileNetworkReply(this, req, op); } + // Return a disabled network reply if network access is disabled. + // Except if the scheme is empty or file://. + if (!d->networkAccessible && !(req.url().scheme() == QLatin1String("file") || + req.url().scheme().isEmpty())) { + return new QDisabledNetworkReply(this, req, op); + } + + if (!d->networkSession && (d->initializeSession || !d->networkConfiguration.isEmpty())) { + QNetworkConfigurationManager manager; + if (!d->networkConfiguration.isEmpty()) { + d->createSession(manager.configurationFromIdentifier(d->networkConfiguration)); + } else { + if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) + d->createSession(manager.defaultConfiguration()); + else + d->initializeSession = false; + } + } + + if (d->networkSession) + d->networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), -1); + QNetworkRequest request = req; if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && outgoingData && !outgoingData->isSequential()) { @@ -716,6 +941,10 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera // first step: create the reply QUrl url = request.url(); QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); + if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) { + connect(this, SIGNAL(networkSessionConnected()), + reply, SLOT(_q_networkSessionConnected())); + } QNetworkReplyImplPrivate *priv = reply->d_func(); priv->manager = this; @@ -750,9 +979,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera void QNetworkAccessManagerPrivate::_q_replyFinished() { Q_Q(QNetworkAccessManager); + QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); if (reply) emit q->finished(reply); + + if (networkSession && q->findChildren<QNetworkReply *>().count() == 1) + networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), 120000); } void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors) @@ -1005,6 +1238,87 @@ QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate() { } +void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config) +{ + Q_Q(QNetworkAccessManager); + + initializeSession = false; + + if (networkSession) + delete networkSession; + + if (!config.isValid()) { + networkSession = 0; + online = false; + + if (networkAccessible == QNetworkAccessManager::NotAccessible) + emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); + else + emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility); + + return; + } + + networkSession = new QNetworkSession(config, q); + + QObject::connect(networkSession, SIGNAL(opened()), q, SIGNAL(networkSessionConnected())); + QObject::connect(networkSession, SIGNAL(closed()), q, SLOT(_q_networkSessionClosed())); + QObject::connect(networkSession, SIGNAL(stateChanged(QNetworkSession::State)), + q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); + QObject::connect(networkSession, SIGNAL(newConfigurationActivated()), + q, SLOT(_q_networkSessionNewConfigurationActivated())); + QObject::connect(networkSession, + SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)), + q, + SLOT(_q_networkSessionPreferredConfigurationChanged(QNetworkConfiguration,bool))); + + _q_networkSessionStateChanged(networkSession->state()); +} + +void QNetworkAccessManagerPrivate::_q_networkSessionClosed() +{ + if (networkSession) { + networkConfiguration = networkSession->configuration().identifier(); + + networkSession->deleteLater(); + networkSession = 0; + } +} + +void QNetworkAccessManagerPrivate::_q_networkSessionNewConfigurationActivated() +{ + Q_Q(QNetworkAccessManager); + + if (networkSession) { + networkSession->accept(); + + emit q->networkSessionConnected(); + } +} + +void QNetworkAccessManagerPrivate::_q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &, bool) +{ + if (networkSession) + networkSession->migrate(); +} + +void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state) +{ + Q_Q(QNetworkAccessManager); + + if (online) { + if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { + online = false; + emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible); + } + } else { + if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) { + online = true; + emit q->networkAccessibleChanged(networkAccessible); + } + } +} + QT_END_NAMESPACE #include "moc_qnetworkaccessmanager.cpp" diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index d2fe527..c57c9c6 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -62,12 +62,16 @@ class QNetworkReply; class QNetworkProxy; class QNetworkProxyFactory; class QSslError; +class QNetworkConfiguration; class QNetworkReplyImplPrivate; class QNetworkAccessManagerPrivate; class Q_NETWORK_EXPORT QNetworkAccessManager: public QObject { Q_OBJECT + + Q_PROPERTY(NetworkAccessibility networkAccessible READ networkAccessible WRITE setNetworkAccessible NOTIFY networkAccessibleChanged) + public: enum Operation { HeadOperation = 1, @@ -75,10 +79,17 @@ public: PutOperation, PostOperation, DeleteOperation, + CustomOperation, UnknownOperation = 0 }; + enum NetworkAccessibility { + UnknownAccessibility = -1, + NotAccessible = 0, + Accessible = 1 + }; + explicit QNetworkAccessManager(QObject *parent = 0); ~QNetworkAccessManager(); @@ -102,6 +113,14 @@ public: QNetworkReply *put(const QNetworkRequest &request, QIODevice *data); QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data); QNetworkReply *deleteResource(const QNetworkRequest &request); + QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = 0); + + void setConfiguration(const QNetworkConfiguration &config); + QNetworkConfiguration configuration() const; + QNetworkConfiguration activeConfiguration() const; + + void setNetworkAccessible(NetworkAccessibility accessible); + NetworkAccessibility networkAccessible() const; Q_SIGNALS: #ifndef QT_NO_NETWORKPROXY @@ -113,6 +132,10 @@ Q_SIGNALS: void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors); #endif + void networkSessionConnected(); + + void networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible); + protected: virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData = 0); @@ -122,6 +145,10 @@ private: Q_DECLARE_PRIVATE(QNetworkAccessManager) Q_PRIVATE_SLOT(d_func(), void _q_replyFinished()) Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>)) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed()) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionNewConfigurationActivated()) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionPreferredConfigurationChanged(QNetworkConfiguration,bool)) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State)) }; QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index 1749373..1785685 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -58,6 +58,7 @@ #include "qnetworkaccessbackend_p.h" #include "private/qobject_p.h" #include "QtNetwork/qnetworkproxy.h" +#include "QtNetwork/qnetworksession.h" QT_BEGIN_NAMESPACE @@ -74,6 +75,10 @@ public: #ifndef QT_NO_NETWORKPROXY proxyFactory(0), #endif + networkSession(0), + networkAccessible(QNetworkAccessManager::Accessible), + online(false), + initializeSession(true), cookieJarCreated(false) { } ~QNetworkAccessManagerPrivate(); @@ -99,6 +104,14 @@ public: QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request); + void createSession(const QNetworkConfiguration &config); + + void _q_networkSessionClosed(); + void _q_networkSessionNewConfigurationActivated(); + void _q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &config, + bool isSeamless); + void _q_networkSessionStateChanged(QNetworkSession::State state); + // this is the cache for storing downloaded files QAbstractNetworkCache *networkCache; @@ -110,8 +123,13 @@ public: QNetworkProxyFactory *proxyFactory; #endif - bool cookieJarCreated; + QNetworkSession *networkSession; + QString networkConfiguration; + QNetworkAccessManager::NetworkAccessibility networkAccessible; + bool online; + bool initializeSession; + bool cookieJarCreated; // this cache can be used by individual backends to cache e.g. their TCP connections to a server // and use the connections for multiple requests. diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index 0a8ea5d..c8b8c1f 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -125,6 +125,11 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() encrypted channel could not be established. The sslErrors() signal should have been emitted. + \value TemporaryNetworkFailureError the connection was broken due + to disconnection from the network, however the system has initiated + roaming to another access point. The request should be resubmitted + and will be processed as soon as the connection is re-established. + \value ProxyConnectionRefusedError the connection to the proxy server was refused (the proxy server is not accepting requests) @@ -533,6 +538,21 @@ QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const return QByteArray(); } +/*! \typedef QNetworkReply::RawHeaderPair + + RawHeaderPair is a QPair<QByteArray, QByteArray> where the first + QByteArray is the header name and the second is the header. + */ + +/*! + Returns a list of raw header pairs. + */ +const QList<QNetworkReply::RawHeaderPair>& QNetworkReply::rawHeaderPairs() const +{ + Q_D(const QNetworkReply); + return d->rawHeaders; +} + /*! Returns a list of headers fields that were sent by the remote server, in the order that they were sent. Duplicate headers are diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h index 5be4cce..acb7379 100644 --- a/src/network/access/qnetworkreply.h +++ b/src/network/access/qnetworkreply.h @@ -77,6 +77,7 @@ public: TimeoutError, OperationCanceledError, SslHandshakeFailedError, + TemporaryNetworkFailureError, UnknownNetworkError = 99, // proxy errors (101-199): @@ -128,6 +129,9 @@ public: QList<QByteArray> rawHeaderList() const; QByteArray rawHeader(const QByteArray &headerName) const; + typedef QPair<QByteArray, QByteArray> RawHeaderPair; + const QList<RawHeaderPair>& rawHeaderPairs() const; + // attributes QVariant attribute(QNetworkRequest::Attribute code) const; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 59c7d76..edd6889 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -46,7 +46,9 @@ #include "QtCore/qcoreapplication.h" #include "QtCore/qdatetime.h" #include "QtNetwork/qsslconfiguration.h" +#include "QtNetwork/qnetworksession.h" #include "qnetworkaccesshttpbackend_p.h" +#include "qnetworkaccessmanager_p.h" #include <QtCore/QCoreApplication> @@ -57,7 +59,7 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() copyDevice(0), cacheEnabled(false), cacheSaveDevice(0), notificationHandlingPaused(false), - bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), + bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1), httpStatusCode(0), state(Idle) { @@ -65,6 +67,8 @@ inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() void QNetworkReplyImplPrivate::_q_startOperation() { + Q_Q(QNetworkReplyImpl); + // ensure this function is only being called once if (state == Working) { qDebug("QNetworkReplyImpl::_q_startOperation was called more than once"); @@ -82,7 +86,27 @@ void QNetworkReplyImplPrivate::_q_startOperation() return; } - backend->open(); + if (!backend->start()) { + // backend failed to start because the session state is not Connected. + // QNetworkAccessManager will call reply->backend->start() again for us when the session + // state changes. + state = WaitingForSession; + + QNetworkSession *session = manager->d_func()->networkSession; + + if (session) { + QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)), + q, SLOT(_q_networkSessionFailed())); + + if (!session->isOpen()) + session->open(); + } else { + qWarning("Backend is waiting for QNetworkSession to connect, but there is none!"); + } + + return; + } + if (state != Finished) { if (operation == QNetworkAccessManager::GetOperation) pendingNotifications.append(NotifyDownstreamReadyWrite); @@ -134,6 +158,8 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead() lastBytesDownloaded = bytesDownloaded; QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); + if (preMigrationDownloaded != Q_INT64_C(-1)) + totalSize = totalSize.toLongLong() + preMigrationDownloaded; pauseNotificationHandling(); emit q->downloadProgress(bytesDownloaded, totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); @@ -206,6 +232,47 @@ void QNetworkReplyImplPrivate::_q_bufferOutgoingData() } } +void QNetworkReplyImplPrivate::_q_networkSessionConnected() +{ + Q_Q(QNetworkReplyImpl); + + if (manager.isNull()) + return; + + QNetworkSession *session = manager->d_func()->networkSession; + if (!session) + return; + + if (session->state() != QNetworkSession::Connected) + return; + + switch (state) { + case QNetworkReplyImplPrivate::Buffering: + case QNetworkReplyImplPrivate::Working: + case QNetworkReplyImplPrivate::Reconnecting: + // Migrate existing downloads to new network connection. + migrateBackend(); + break; + case QNetworkReplyImplPrivate::WaitingForSession: + // Start waiting requests. + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); + break; + default: + ; + } +} + +void QNetworkReplyImplPrivate::_q_networkSessionFailed() +{ + // Abort waiting replies. + if (state == WaitingForSession) { + state = Working; + error(QNetworkReplyImpl::UnknownNetworkError, + QCoreApplication::translate("QNetworkReply", "Network session error.")); + finished(); + } +} + void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *data) { @@ -251,11 +318,15 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const // for HTTP, we want to send out the request as fast as possible to the network, without // invoking methods in a QueuedConnection +#ifndef QT_NO_HTTP if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) { _q_startOperation(); } else { QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); } +#else + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); +#endif // QT_NO_HTTP } q->QIODevice::open(QIODevice::ReadOnly); @@ -457,6 +528,8 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data) QPointer<QNetworkReplyImpl> qq = q; QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); + if (preMigrationDownloaded != Q_INT64_C(-1)) + totalSize = totalSize.toLongLong() + preMigrationDownloaded; pauseNotificationHandling(); emit q->downloadProgress(bytesDownloaded, totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); @@ -498,14 +571,42 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data) void QNetworkReplyImplPrivate::finished() { Q_Q(QNetworkReplyImpl); - if (state == Finished || state == Aborted) + + if (state == Finished || state == Aborted || state == WaitingForSession) return; + pauseNotificationHandling(); + QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); + if (preMigrationDownloaded != Q_INT64_C(-1)) + totalSize = totalSize.toLongLong() + preMigrationDownloaded; + + if (!manager.isNull()) { + QNetworkSession *session = manager->d_func()->networkSession; + if (session && session->state() == QNetworkSession::Roaming && + state == Working && errorCode != QNetworkReply::OperationCanceledError) { + // only content with a known size will fail with a temporary network failure error + if (!totalSize.isNull()) { + if (bytesDownloaded != totalSize) { + if (migrateBackend()) { + // either we are migrating or the request is finished/aborted + if (state == Reconnecting || state == WaitingForSession) { + resumeNotificationHandling(); + return; // exit early if we are migrating. + } + } else { + error(QNetworkReply::TemporaryNetworkFailureError, + QNetworkReply::tr("Temporary network failure.")); + } + } + } + } + } + resumeNotificationHandling(); + state = Finished; pendingNotifications.clear(); pauseNotificationHandling(); - QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); if (totalSize.isNull() || totalSize == -1) { emit q->downloadProgress(bytesDownloaded, bytesDownloaded); } @@ -514,7 +615,9 @@ void QNetworkReplyImplPrivate::finished() emit q->uploadProgress(0, 0); resumeNotificationHandling(); - completeCacheSave(); + // if we don't know the total size of or we received everything save the cache + if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize) + completeCacheSave(); // note: might not be a good idea, since users could decide to delete us // which would delete the backend too... @@ -634,6 +737,13 @@ void QNetworkReplyImpl::close() d->finished(); } +bool QNetworkReplyImpl::canReadLine () const +{ + Q_D(const QNetworkReplyImpl); + return QNetworkReply::canReadLine() || d->readBuffer.canReadLine(); +} + + /*! Returns the number of bytes available for reading with QIODevice::read(). The number of bytes available may grow until @@ -722,6 +832,87 @@ bool QNetworkReplyImpl::event(QEvent *e) return QObject::event(e); } +/* + Migrates the backend of the QNetworkReply to a new network connection if required. Returns + true if the reply is migrated or it is not required; otherwise returns false. +*/ +bool QNetworkReplyImplPrivate::migrateBackend() +{ + Q_Q(QNetworkReplyImpl); + + // Network reply is already finished or aborted, don't need to migrate. + if (state == Finished || state == Aborted) + return true; + + // Backend does not support resuming download. + if (!backend->canResume()) + return false; + + // Request has outgoing data, not migrating. + if (outgoingData) + return false; + + // Request is serviced from the cache, don't need to migrate. + if (copyDevice) + return true; + + state = QNetworkReplyImplPrivate::Reconnecting; + + if (backend) { + delete backend; + backend = 0; + } + + cookedHeaders.clear(); + rawHeaders.clear(); + + preMigrationDownloaded = bytesDownloaded; + + backend = manager->d_func()->findBackend(operation, request); + + if (backend) { + backend->setParent(q); + backend->reply = this; + backend->setResumeOffset(bytesDownloaded); + } + +#ifndef QT_NO_HTTP + if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) { + _q_startOperation(); + } else { + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); + } +#else + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); +#endif // QT_NO_HTTP + + return true; +} + +QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent, + const QNetworkRequest &req, + QNetworkAccessManager::Operation op) +: QNetworkReply(parent) +{ + setRequest(req); + setUrl(req.url()); + setOperation(op); + + qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); + + QString msg = QCoreApplication::translate("QNetworkAccessManager", + "Network access is disabled."); + setError(UnknownNetworkError, msg); + + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError)); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); +} + +QDisabledNetworkReply::~QDisabledNetworkReply() +{ +} + QT_END_NAMESPACE #include "moc_qnetworkreplyimpl_p.cpp" diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index 168e5cf..fcb3397 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -77,10 +77,11 @@ public: ~QNetworkReplyImpl(); virtual void abort(); - // reimplemented from QNetworkReply + // reimplemented from QNetworkReply / QIODevice virtual void close(); virtual qint64 bytesAvailable() const; virtual void setReadBufferSize(qint64 size); + virtual bool canReadLine () const; virtual qint64 readData(char *data, qint64 maxlen); virtual bool event(QEvent *); @@ -98,6 +99,8 @@ public: Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished()) Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData()) Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished()) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected()) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed()) }; class QNetworkReplyImplPrivate: public QNetworkReplyPrivate @@ -110,11 +113,13 @@ public: }; enum State { - Idle, - Buffering, - Working, - Finished, - Aborted + Idle, // The reply is idle. + Buffering, // The reply is buffering outgoing data. + Working, // The reply is uploading/downloading data. + Finished, // The reply has finished. + Aborted, // The reply has been aborted. + WaitingForSession, // The reply is waiting for the session to open before connecting. + Reconnecting // The reply will reconnect to once roaming has completed. }; typedef QQueue<InternalNotifications> NotificationQueue; @@ -128,6 +133,8 @@ public: void _q_copyReadChannelFinished(); void _q_bufferOutgoingData(); void _q_bufferOutgoingDataFinished(); + void _q_networkSessionConnected(); + void _q_networkSessionFailed(); void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData); @@ -161,6 +168,8 @@ public: QIODevice *copyDevice; QAbstractNetworkCache *networkCache() const; + bool migrateBackend(); + bool cacheEnabled; QIODevice *cacheSaveDevice; @@ -177,6 +186,7 @@ public: qint64 bytesDownloaded; qint64 lastBytesDownloaded; qint64 bytesUploaded; + qint64 preMigrationDownloaded; QString httpReasonPhrase; int httpStatusCode; @@ -186,6 +196,20 @@ public: Q_DECLARE_PUBLIC(QNetworkReplyImpl) }; +class QDisabledNetworkReply : public QNetworkReply +{ + Q_OBJECT + +public: + QDisabledNetworkReply(QObject *parent, const QNetworkRequest &req, + QNetworkAccessManager::Operation op); + ~QDisabledNetworkReply(); + + void abort() { } +protected: + qint64 readData(char *, qint64) { return -1; } +}; + QT_END_NAMESPACE #endif diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index c4ff24d..61c116d 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -184,6 +184,12 @@ QT_BEGIN_NAMESPACE Indicates whether the HTTP pipelining was used for receiving this reply. + \value CustomVerbAttribute + Requests only, type: QVariant::ByteArray + Holds the value for the custom HTTP verb to send (destined for usage + of other verbs than GET, POST, PUT and DELETE). This verb is set + when calling QNetworkAccessManager::sendCustomRequest(). + \value User Special type. Additional information can be passed in QVariants with types ranging from User to UserMax. The default @@ -220,8 +226,9 @@ class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate { public: inline QNetworkRequestPrivate() + : priority(QNetworkRequest::NormalPriority) #ifndef QT_NO_OPENSSL - : sslConfiguration(0) + , sslConfiguration(0) #endif { qRegisterMetaType<QNetworkRequest>(); } ~QNetworkRequestPrivate() @@ -236,6 +243,7 @@ public: : QSharedData(other), QNetworkHeadersPrivate(other) { url = other.url; + priority = other.priority; #ifndef QT_NO_OPENSSL sslConfiguration = 0; @@ -247,12 +255,14 @@ public: inline bool operator==(const QNetworkRequestPrivate &other) const { return url == other.url && + priority == other.priority && rawHeaders == other.rawHeaders && attributes == other.attributes; // don't compare cookedHeaders } QUrl url; + QNetworkRequest::Priority priority; #ifndef QT_NO_OPENSSL mutable QSslConfiguration *sslConfiguration; #endif @@ -518,6 +528,45 @@ QObject *QNetworkRequest::originatingObject() const return d->originatingObject.data(); } +/*! + \since 4.7 + + Return the priority of this request. + + \sa setPriority() +*/ +QNetworkRequest::Priority QNetworkRequest::priority() const +{ + return d->priority; +} + +/*! \enum QNetworkRequest::Priority + + \since 4.7 + + This enum lists the possible network request priorities. + + \value HighPriority High priority + \value NormalPriority Normal priority + \value LowPriority Low priority + */ + +/*! + \since 4.7 + + Set the priority of this request to \a priority. + + \note The \a priority is only a hint to the network access + manager. It can use it or not. Currently it is used for HTTP to + decide which request should be sent first to a server. + + \sa priority() +*/ +void QNetworkRequest::setPriority(Priority priority) +{ + d->priority = priority; +} + static QByteArray headerName(QNetworkRequest::KnownHeaders header) { switch (header) { diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index fe01f8c..a0ef1a6 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -78,6 +78,7 @@ public: DoNotBufferUploadDataAttribute, HttpPipeliningAllowedAttribute, HttpPipeliningWasUsedAttribute, + CustomVerbAttribute, User = 1000, UserMax = 32767 @@ -89,6 +90,12 @@ public: AlwaysCache }; + enum Priority { + HighPriority = 1, + NormalPriority = 3, + LowPriority = 5 + }; + explicit QNetworkRequest(const QUrl &url = QUrl()); QNetworkRequest(const QNetworkRequest &other); ~QNetworkRequest(); @@ -123,6 +130,9 @@ public: void setOriginatingObject(QObject *object); QObject *originatingObject() const; + Priority priority() const; + void setPriority(Priority priority); + private: QSharedDataPointer<QNetworkRequestPrivate> d; friend class QNetworkRequestPrivate; diff --git a/src/network/bearer/bearer.pri b/src/network/bearer/bearer.pri new file mode 100644 index 0000000..44e97fd --- /dev/null +++ b/src/network/bearer/bearer.pri @@ -0,0 +1,18 @@ +# Qt network bearer management module + +HEADERS += bearer/qnetworkconfiguration.h \ + bearer/qnetworksession.h \ + bearer/qnetworkconfigmanager.h \ + bearer/qnetworkconfigmanager_p.h \ + bearer/qnetworkconfiguration_p.h \ + bearer/qnetworksession_p.h \ + bearer/qbearerengine_p.h \ + bearer/qbearerplugin_p.h + +SOURCES += bearer/qnetworksession.cpp \ + bearer/qnetworkconfigmanager.cpp \ + bearer/qnetworkconfiguration.cpp \ + bearer/qnetworkconfigmanager_p.cpp \ + bearer/qbearerengine.cpp \ + bearer/qbearerplugin.cpp + diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp new file mode 100644 index 0000000..c42e2d2 --- /dev/null +++ b/src/network/bearer/qbearerengine.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbearerengine_p.h" + +QT_BEGIN_NAMESPACE + +QBearerEngine::QBearerEngine(QObject *parent) +: QObject(parent), mutex(QMutex::Recursive) +{ +} + +QBearerEngine::~QBearerEngine() +{ + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it; + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end; + for (it = snapConfigurations.begin(), end = snapConfigurations.end(); it != end; ++it) { + it.value()->isValid = false; + it.value()->id.clear(); + } + + for (it = accessPointConfigurations.begin(), end = accessPointConfigurations.end(); + it != end; ++it) { + it.value()->isValid = false; + it.value()->id.clear(); + } + + for (it = userChoiceConfigurations.begin(), end = userChoiceConfigurations.end(); + it != end; ++it) { + it.value()->isValid = false; + it.value()->id.clear(); + } +} + +bool QBearerEngine::requiresPolling() const +{ + return false; +} + +/* + Returns true if configurations are in use; otherwise returns false. + + If configurations are in use and requiresPolling() returns true, polling will be enabled for + this engine. +*/ +bool QBearerEngine::configurationsInUse() const +{ + QHash<QString, QNetworkConfigurationPrivatePointer>::ConstIterator it; + QHash<QString, QNetworkConfigurationPrivatePointer>::ConstIterator end; + + QMutexLocker locker(&mutex); + + for (it = accessPointConfigurations.begin(), + end = accessPointConfigurations.end(); it != end; ++it) { + if (it.value()->ref > 1) + return true; + } + + for (it = snapConfigurations.begin(), end = snapConfigurations.end(); it != end; ++it) { + if (it.value()->ref > 1) + return true; + } + + for (it = userChoiceConfigurations.begin(), + end = userChoiceConfigurations.end(); it != end; ++it) { + if (it.value()->ref > 1) + return true; + } + + return false; +} + +#include "moc_qbearerengine_p.cpp" + +QT_END_NAMESPACE diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h new file mode 100644 index 0000000..fd4bf87 --- /dev/null +++ b/src/network/bearer/qbearerengine_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBEARERENGINE_P_H +#define QBEARERENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkconfiguration_p.h" +#include "qnetworksession.h" +#include "qnetworkconfigmanager.h" + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qlist.h> +#include <QtCore/qstring.h> +#include <QtCore/qhash.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +class QNetworkConfiguration; + +class Q_NETWORK_EXPORT QBearerEngine : public QObject +{ + Q_OBJECT + + friend class QNetworkConfigurationManagerPrivate; + +public: + QBearerEngine(QObject *parent = 0); + virtual ~QBearerEngine(); + + virtual bool hasIdentifier(const QString &id) = 0; + + virtual QNetworkConfigurationManager::Capabilities capabilities() const = 0; + + virtual QNetworkSessionPrivate *createSessionBackend() = 0; + + virtual QNetworkConfigurationPrivatePointer defaultConfiguration() = 0; + + virtual bool requiresPolling() const; + bool configurationsInUse() const; + +Q_SIGNALS: + void configurationAdded(QNetworkConfigurationPrivatePointer config); + void configurationRemoved(QNetworkConfigurationPrivatePointer config); + void configurationChanged(QNetworkConfigurationPrivatePointer config); + + void updateCompleted(); + +protected: + //this table contains an up to date list of all configs at any time. + //it must be updated if configurations change, are added/removed or + //the members of ServiceNetworks change + QHash<QString, QNetworkConfigurationPrivatePointer> accessPointConfigurations; + QHash<QString, QNetworkConfigurationPrivatePointer> snapConfigurations; + QHash<QString, QNetworkConfigurationPrivatePointer> userChoiceConfigurations; + + mutable QMutex mutex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/bearer/qbearerplugin.cpp b/src/network/bearer/qbearerplugin.cpp new file mode 100644 index 0000000..4509fd0 --- /dev/null +++ b/src/network/bearer/qbearerplugin.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbearerplugin_p.h" + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +QBearerEnginePlugin::QBearerEnginePlugin(QObject *parent) +: QObject(parent) +{ +} + +QBearerEnginePlugin::~QBearerEnginePlugin() +{ +} + +QT_END_NAMESPACE diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h new file mode 100644 index 0000000..36709c2 --- /dev/null +++ b/src/network/bearer/qbearerplugin_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBEARERPLUGIN_P_H +#define QBEARERPLUGIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qbearerengine_p.h" + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +struct Q_NETWORK_EXPORT QBearerEngineFactoryInterface : public QFactoryInterface +{ + virtual QBearerEngine *create(const QString &key = QString()) const = 0; +}; + +#define QBearerEngineFactoryInterface_iid "com.trolltech.Qt.QBearerEngineFactoryInterface" +Q_DECLARE_INTERFACE(QBearerEngineFactoryInterface, QBearerEngineFactoryInterface_iid) + +class Q_NETWORK_EXPORT QBearerEnginePlugin : public QObject, public QBearerEngineFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QBearerEngineFactoryInterface:QFactoryInterface) + +public: + explicit QBearerEnginePlugin(QObject *parent = 0); + virtual ~QBearerEnginePlugin(); + + virtual QStringList keys() const = 0; + virtual QBearerEngine *create(const QString &key = QString()) const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp new file mode 100644 index 0000000..1ba5dab --- /dev/null +++ b/src/network/bearer/qnetworkconfigmanager.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkconfigmanager.h" + +#include "qnetworkconfigmanager_p.h" +#include "qbearerengine_p.h" + +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QNetworkConfigurationManagerPrivate, connManager); + +QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate() +{ + return connManager(); +} + +/*! + \class QNetworkConfigurationManager + + \brief The QNetworkConfigurationManager class manages the network configurations provided + by the system. + + \since 4.7 + + \inmodule QtNetwork + \ingroup network + + QNetworkConfigurationManager provides access to the network configurations known to the system and + enables applications to detect the system capabilities (with regards to network sessions) at runtime. + + A QNetworkConfiguration abstracts a set of configuration options describing how a + network interface has to be configured to connect to a particular target network. + QNetworkConfigurationManager maintains and updates the global list of + QNetworkConfigurations. Applications can access and filter this list via + allConfigurations(). If a new configuration is added or an existing one is removed or changed + the configurationAdded(), configurationRemoved() and configurationChanged() signals are emitted + respectively. + + The defaultConfiguration() can be used when intending to immediately create a new + network session without caring about the particular configuration. It returns + a \l QNetworkConfiguration::Discovered configuration. If there are not any + discovered ones an invalid configuration is returned. + + Some configuration updates may require some time to perform updates. A WLAN scan is + such an example. Unless the platform performs internal updates it may be required to + manually trigger configuration updates via QNetworkConfigurationManager::updateConfigurations(). + The completion of the update process is indicted by emitting the updateCompleted() + signal. The update process ensures that every existing QNetworkConfiguration instance + is updated. There is no need to ask for a renewed configuration list via allConfigurations(). + + \sa QNetworkConfiguration +*/ + +/*! + \fn void QNetworkConfigurationManager::configurationAdded(const QNetworkConfiguration& config) + + This signal is emitted whenever a new network configuration is added to the system. The new + configuration is specified by \a config. +*/ + +/*! + \fn void QNetworkConfigurationManager::configurationRemoved(const QNetworkConfiguration& configuration) + + This signal is emitted when a configuration is about to be removed from the system. The removed + \a configuration is invalid but retains name and identifier. +*/ + +/*! + \fn void QNetworkConfigurationManager::updateCompleted() + + This signal is emitted when the configuration update has been completed. Such an update can + be initiated via \l updateConfigurations(). +*/ + +/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration& config) + + This signal is emitted when the \l {QNetworkConfiguration::state()}{state} of \a config changes. +*/ + +/*! + \fn void QNetworkConfigurationManager::onlineStateChanged(bool isOnline) + + This signal is emitted when the device changes from online to offline mode or vice versa. + \a isOnline represents the new state of the device. + + The state is considered to be online for as long as + \l{allConfigurations()}{allConfigurations}(QNetworkConfiguration::Active) returns a list with + at least one entry. +*/ + +/*! + \enum QNetworkConfigurationManager::Capability + + Specifies the system capabilities of the bearer API. The possible values are: + + \value CanStartAndStopInterfaces Network sessions and their underlying access points can be + started and stopped. If this flag is not set QNetworkSession + can only monitor but not influence the state of access points. + On some platforms this feature may require elevated user + permissions. This option is platform specific and may not + always be available. + \value DirectConnectionRouting Network sessions and their sockets can be bound to a + particular network interface. Any packet that passes through + the socket goes to the specified network interface and thus + disregards standard routing table entries. This may be useful + when two interfaces can reach overlapping IP ranges or an + application has specific needs in regards to target networks. + This option is platform specific and may not always be + available. + \value SystemSessionSupport If this flag is set the underlying platform ensures that a + network interface is not shut down until the last network + session has been \l{QNetworkSession::close()}{closed()}. This + works across multiple processes. If the platform session + support is missing this API can only ensure the above behavior + for network sessions within the same process. + In general mobile platforms (such as Symbian/S60) have such + support whereas most desktop platform lack this capability. + \value ApplicationLevelRoaming The system gives applications control over the systems roaming + behavior. Applications can initiate roaming (in case the + current link is not suitable) and are consulted if the system + has identified a more suitable access point. + \value ForcedRoaming The system disconnects an existing access point and reconnects + via a more suitable one. The application does not have any + control over this process and has to reconnect its active + sockets. + \value DataStatistics If this flag is set QNetworkSession can provide statistics + about transmitted and received data. + \value NetworkSessionRequired If this flag is set the platform requires that a network + session is created before network operations can be performed. +*/ + +/*! + Constructs a QNetworkConfigurationManager with the given \a parent. +*/ +QNetworkConfigurationManager::QNetworkConfigurationManager( QObject* parent ) + : QObject(parent) +{ + QNetworkConfigurationManagerPrivate *priv = connManager(); + + connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)), + this, SIGNAL(configurationAdded(QNetworkConfiguration))); + connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)), + this, SIGNAL(configurationRemoved(QNetworkConfiguration))); + connect(priv, SIGNAL(configurationUpdateComplete()), + this, SIGNAL(updateCompleted())); + connect(priv, SIGNAL(onlineStateChanged(bool)), + this, SIGNAL(onlineStateChanged(bool))); + connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)), + this, SIGNAL(configurationChanged(QNetworkConfiguration))); + + priv->enablePolling(); +} + +/*! + Frees the resources associated with the QNetworkConfigurationManager object. +*/ +QNetworkConfigurationManager::~QNetworkConfigurationManager() +{ + QNetworkConfigurationManagerPrivate *priv = connManager(); + + priv->disablePolling(); +} + + +/*! + Returns the default configuration to be used. This function always returns a discovered + configuration; otherwise an invalid configuration. + + In some cases it may be required to call updateConfigurations() and wait for the + updateCompleted() signal before calling this function. + + \sa allConfigurations() +*/ +QNetworkConfiguration QNetworkConfigurationManager::defaultConfiguration() const +{ + return connManager()->defaultConfiguration(); +} + +/*! + Returns the list of configurations which comply with the given \a filter. + + By default this function returns all (defined and undefined) configurations. + + A wireless network with a particular SSID may only be accessible in a + certain area despite the fact that the system has a valid configuration + for it. Therefore the filter flag may be used to limit the list to + discovered and possibly connected configurations only. + + If \a filter is set to zero this function returns all possible configurations. + + Note that this function returns the states for all configurations as they are known at + the time of this function call. If for instance a configuration of type WLAN is defined + the system may have to perform a WLAN scan in order to determine whether it is + actually available. To obtain the most accurate state updateConfigurations() should + be used to update each configuration's state. Note that such an update may require + some time. It's completion is signalled by updateCompleted(). In the absence of a + configuration update this function returns the best estimate at the time of the call. + Therefore, if WLAN configurations are of interest, it is recommended that + updateConfigurations() is called once after QNetworkConfigurationManager + instantiation (WLAN scans are too time consuming to perform in constructor). + After this the data is kept automatically up-to-date as the system reports + any changes. +*/ +QList<QNetworkConfiguration> QNetworkConfigurationManager::allConfigurations(QNetworkConfiguration::StateFlags filter) const +{ + return connManager()->allConfigurations(filter); +} + +/*! + Returns the QNetworkConfiguration for \a identifier; otherwise returns an + invalid QNetworkConfiguration. + + \sa QNetworkConfiguration::identifier() +*/ +QNetworkConfiguration QNetworkConfigurationManager::configurationFromIdentifier(const QString &identifier) const +{ + return connManager()->configurationFromIdentifier(identifier); +} + +/*! + Returns true if the system is considered to be connected to another device via an active + network interface; otherwise returns false. + + This is equivalent to the following code snippet: + + \snippet doc/src/snippets/code/src_network_bearer_qnetworkconfigmanager.cpp 0 + + \sa onlineStateChanged() +*/ +bool QNetworkConfigurationManager::isOnline() const +{ + return connManager()->isOnline(); +} + +/*! + Returns the capabilities supported by the current platform. +*/ +QNetworkConfigurationManager::Capabilities QNetworkConfigurationManager::capabilities() const +{ + return connManager()->capFlags; +} + +/*! + Initiates an update of all configurations. This may be used to initiate WLAN scans or other + time consuming updates which may be required to obtain the correct state for configurations. + + This call is asynchronous. On completion of this update the updateCompleted() signal is + emitted. If new configurations are discovered or old ones were removed or changed the update + process may trigger the emission of one or multiple configurationAdded(), + configurationRemoved() and configurationChanged() signals. + + If a configuration state changes as a result of this update all existing QNetworkConfiguration + instances are updated automatically. + + \sa allConfigurations() +*/ +void QNetworkConfigurationManager::updateConfigurations() +{ + connManager()->performAsyncConfigurationUpdate(); +} + +#include "moc_qnetworkconfigmanager.cpp" + +QT_END_NAMESPACE + diff --git a/src/network/bearer/qnetworkconfigmanager.h b/src/network/bearer/qnetworkconfigmanager.h new file mode 100644 index 0000000..bb4d8a0 --- /dev/null +++ b/src/network/bearer/qnetworkconfigmanager.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKCONFIGURATIONMANAGER_H +#define QNETWORKCONFIGURATIONMANAGER_H + +#include <QtCore/qobject.h> +#include <QtNetwork/qnetworkconfiguration.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QNetworkConfigurationManagerPrivate; +class Q_NETWORK_EXPORT QNetworkConfigurationManager : public QObject +{ + Q_OBJECT + +public: + + enum Capability { + CanStartAndStopInterfaces = 0x00000001, + DirectConnectionRouting = 0x00000002, + SystemSessionSupport = 0x00000004, + ApplicationLevelRoaming = 0x00000008, + ForcedRoaming = 0x00000010, + DataStatistics = 0x00000020, + NetworkSessionRequired = 0x00000040 + }; + + Q_DECLARE_FLAGS(Capabilities, Capability) + + QNetworkConfigurationManager( QObject* parent = 0 ); + virtual ~QNetworkConfigurationManager(); + + + QNetworkConfigurationManager::Capabilities capabilities() const; + + QNetworkConfiguration defaultConfiguration() const; + QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags flags = 0) const; + QNetworkConfiguration configurationFromIdentifier(const QString& identifier) const; + void updateConfigurations(); + + bool isOnline() const; + +Q_SIGNALS: + void configurationAdded(const QNetworkConfiguration& config); + void configurationRemoved(const QNetworkConfiguration& config); + void configurationChanged(const QNetworkConfiguration& config); + void onlineStateChanged(bool isOnline); + void updateCompleted(); + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkConfigurationManager::Capabilities) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QNETWORKCONFIGURATIONMANAGER_H + diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp new file mode 100644 index 0000000..c665fa2 --- /dev/null +++ b/src/network/bearer/qnetworkconfigmanager_p.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkconfigmanager_p.h" +#include "qbearerplugin_p.h" + +#include <QtCore/private/qfactoryloader_p.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qthread.h> +#include <QtCore/private/qcoreapplication_p.h> + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QBearerEngineFactoryInterface_iid, QLatin1String("/bearer"))) + +QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate() +: capFlags(0), mutex(QMutex::Recursive), pollTimer(0), forcedPolling(0), firstUpdate(true) +{ + qRegisterMetaType<QNetworkConfiguration>("QNetworkConfiguration"); + + moveToThread(QCoreApplicationPrivate::mainThread()); + updateConfigurations(); +} + +QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() +{ + QMutexLocker locker(&mutex); + + qDeleteAll(sessionEngines); +} + +QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() +{ + QMutexLocker locker(&mutex); + + foreach (QBearerEngine *engine, sessionEngines) { + QNetworkConfigurationPrivatePointer ptr = engine->defaultConfiguration(); + + if (ptr) { + QNetworkConfiguration config; + config.d = ptr; + return config; + } + } + + // Engines don't have a default configuration. + + // Return first active snap + QNetworkConfigurationPrivatePointer defaultConfiguration; + + foreach (QBearerEngine *engine, sessionEngines) { + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it; + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end; + + QMutexLocker locker(&engine->mutex); + + for (it = engine->snapConfigurations.begin(), end = engine->snapConfigurations.end(); + it != end; ++it) { + QNetworkConfigurationPrivatePointer ptr = it.value(); + + QMutexLocker configLocker(&ptr->mutex); + + if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { + QNetworkConfiguration config; + config.d = ptr; + return config; + } else if (!defaultConfiguration) { + if ((ptr->state & QNetworkConfiguration::Discovered) == + QNetworkConfiguration::Discovered) { + defaultConfiguration = ptr; + } + } + } + } + + // No Active SNAPs return first Discovered SNAP. + if (defaultConfiguration) { + QNetworkConfiguration config; + config.d = defaultConfiguration; + return config; + } + + /* + No Active or Discovered SNAPs, find the perferred access point. + The following priority order is used: + + 1. Active Ethernet + 2. Active WLAN + 3. Active Other + 4. Discovered Ethernet + 5. Discovered WLAN + 6. Discovered Other + */ + + defaultConfiguration.reset(); + + foreach (QBearerEngine *engine, sessionEngines) { + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it; + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end; + + QMutexLocker locker(&engine->mutex); + + for (it = engine->accessPointConfigurations.begin(), + end = engine->accessPointConfigurations.end(); it != end; ++it) { + QNetworkConfigurationPrivatePointer ptr = it.value(); + + const QString bearerName = ptr->bearerName(); + QMutexLocker configLocker(&ptr->mutex); + + if ((ptr->state & QNetworkConfiguration::Discovered) == + QNetworkConfiguration::Discovered) { + if (!defaultConfiguration) { + defaultConfiguration = ptr; + } else { + if (defaultConfiguration->state == ptr->state) { + if (defaultConfiguration->bearerName() == QLatin1String("Ethernet")) { + // do nothing + } else if (defaultConfiguration->bearerName() == QLatin1String("WLAN")) { + // ethernet beats wlan + if (bearerName == QLatin1String("Ethernet")) + defaultConfiguration = ptr; + } else { + // ethernet and wlan beats other + if (bearerName == QLatin1String("Ethernet") || + bearerName == QLatin1String("WLAN")) { + defaultConfiguration = ptr; + } + } + } else { + // active beats discovered + if ((defaultConfiguration->state & QNetworkConfiguration::Active) != + QNetworkConfiguration::Active) { + defaultConfiguration = ptr; + } + } + } + } + } + } + + // No Active InternetAccessPoint return first Discovered InternetAccessPoint. + if (defaultConfiguration) { + QNetworkConfiguration config; + config.d = defaultConfiguration; + return config; + } + + return QNetworkConfiguration(); +} + +QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) +{ + QList<QNetworkConfiguration> result; + + QMutexLocker locker(&mutex); + + foreach (QBearerEngine *engine, sessionEngines) { + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it; + QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end; + + QMutexLocker locker(&engine->mutex); + + //find all InternetAccessPoints + for (it = engine->accessPointConfigurations.begin(), + end = engine->accessPointConfigurations.end(); it != end; ++it) { + QNetworkConfigurationPrivatePointer ptr = it.value(); + + QMutexLocker configLocker(&ptr->mutex); + + if ((ptr->state & filter) == filter) { + QNetworkConfiguration pt; + pt.d = ptr; + result << pt; + } + } + + //find all service networks + for (it = engine->snapConfigurations.begin(), + end = engine->snapConfigurations.end(); it != end; ++it) { + QNetworkConfigurationPrivatePointer ptr = it.value(); + + QMutexLocker configLocker(&ptr->mutex); + + if ((ptr->state & filter) == filter) { + QNetworkConfiguration pt; + pt.d = ptr; + result << pt; + } + } + } + + return result; +} + +QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) +{ + QNetworkConfiguration item; + + QMutexLocker locker(&mutex); + + foreach (QBearerEngine *engine, sessionEngines) { + QMutexLocker locker(&engine->mutex); + + if (engine->accessPointConfigurations.contains(identifier)) + item.d = engine->accessPointConfigurations.value(identifier); + else if (engine->snapConfigurations.contains(identifier)) + item.d = engine->snapConfigurations.value(identifier); + else if (engine->userChoiceConfigurations.contains(identifier)) + item.d = engine->userChoiceConfigurations.value(identifier); + else + continue; + + return item; + } + + return item; +} + +bool QNetworkConfigurationManagerPrivate::isOnline() +{ + QMutexLocker locker(&mutex); + + return !onlineConfigurations.isEmpty(); +} + +void QNetworkConfigurationManagerPrivate::configurationAdded(QNetworkConfigurationPrivatePointer ptr) +{ + QMutexLocker locker(&mutex); + + if (!firstUpdate) { + QNetworkConfiguration item; + item.d = ptr; + emit configurationAdded(item); + } + + ptr->mutex.lock(); + if (ptr->state == QNetworkConfiguration::Active) { + ptr->mutex.unlock(); + onlineConfigurations.insert(ptr->id); + if (!firstUpdate && onlineConfigurations.count() == 1) + emit onlineStateChanged(true); + } else { + ptr->mutex.unlock(); + } +} + +void QNetworkConfigurationManagerPrivate::configurationRemoved(QNetworkConfigurationPrivatePointer ptr) +{ + QMutexLocker locker(&mutex); + + ptr->mutex.lock(); + ptr->isValid = false; + ptr->mutex.unlock(); + + if (!firstUpdate) { + QNetworkConfiguration item; + item.d = ptr; + emit configurationRemoved(item); + } + + onlineConfigurations.remove(ptr->id); + if (!firstUpdate && onlineConfigurations.isEmpty()) + emit onlineStateChanged(false); +} + +void QNetworkConfigurationManagerPrivate::configurationChanged(QNetworkConfigurationPrivatePointer ptr) +{ + QMutexLocker locker(&mutex); + + if (!firstUpdate) { + QNetworkConfiguration item; + item.d = ptr; + emit configurationChanged(item); + } + + bool previous = !onlineConfigurations.isEmpty(); + + ptr->mutex.lock(); + if (ptr->state == QNetworkConfiguration::Active) + onlineConfigurations.insert(ptr->id); + else + onlineConfigurations.remove(ptr->id); + ptr->mutex.unlock(); + + bool online = !onlineConfigurations.isEmpty(); + + if (!firstUpdate && online != previous) + emit onlineStateChanged(online); +} + +void QNetworkConfigurationManagerPrivate::updateConfigurations() +{ + QMutexLocker locker(&mutex); + + if (firstUpdate) { + if (sender()) + return; + + updating = false; + + QFactoryLoader *l = loader(); + + QBearerEngine *generic = 0; + + foreach (const QString &key, l->keys()) { + QBearerEnginePlugin *plugin = qobject_cast<QBearerEnginePlugin *>(l->instance(key)); + if (plugin) { + QBearerEngine *engine = plugin->create(key); + if (!engine) + continue; + + if (key == QLatin1String("generic")) + generic = engine; + else + sessionEngines.append(engine); + + engine->moveToThread(QCoreApplicationPrivate::mainThread()); + + connect(engine, SIGNAL(updateCompleted()), + this, SLOT(updateConfigurations())); + connect(engine, SIGNAL(configurationAdded(QNetworkConfigurationPrivatePointer)), + this, SLOT(configurationAdded(QNetworkConfigurationPrivatePointer))); + connect(engine, SIGNAL(configurationRemoved(QNetworkConfigurationPrivatePointer)), + this, SLOT(configurationRemoved(QNetworkConfigurationPrivatePointer))); + connect(engine, SIGNAL(configurationChanged(QNetworkConfigurationPrivatePointer)), + this, SLOT(configurationChanged(QNetworkConfigurationPrivatePointer))); + + capFlags |= engine->capabilities(); + + QMetaObject::invokeMethod(engine, "requestUpdate"); + } + } + + if (generic) + sessionEngines.append(generic); + } + + QBearerEngine *engine = qobject_cast<QBearerEngine *>(sender()); + if (!updatingEngines.isEmpty() && engine) { + int index = sessionEngines.indexOf(engine); + if (index >= 0) + updatingEngines.remove(index); + } + + if (updating && updatingEngines.isEmpty()) { + updating = false; + emit configurationUpdateComplete(); + } + + if (engine && !pollingEngines.isEmpty()) { + int index = sessionEngines.indexOf(engine); + if (index >= 0) + pollingEngines.remove(index); + + if (pollingEngines.isEmpty()) + startPolling(); + } + + if (firstUpdate) + firstUpdate = false; +} + +void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate() +{ + QMutexLocker locker(&mutex); + + if (sessionEngines.isEmpty()) { + emit configurationUpdateComplete(); + return; + } + + updating = true; + + for (int i = 0; i < sessionEngines.count(); ++i) { + updatingEngines.insert(i); + QMetaObject::invokeMethod(sessionEngines.at(i), "requestUpdate"); + } +} + +QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() +{ + QMutexLocker locker(&mutex); + + return sessionEngines; +} + +void QNetworkConfigurationManagerPrivate::startPolling() +{ + QMutexLocker locker(&mutex); + + bool pollingRequired = false; + + if (forcedPolling > 0) { + foreach (QBearerEngine *engine, sessionEngines) { + if (engine->requiresPolling()) { + pollingRequired = true; + break; + } + } + } + + if (!pollingRequired) { + foreach (QBearerEngine *engine, sessionEngines) { + if (engine->configurationsInUse()) { + pollingRequired = true; + break; + } + } + } + + if (pollingRequired) { + if (!pollTimer) { + pollTimer = new QTimer(this); + pollTimer->setInterval(10000); + pollTimer->setSingleShot(true); + connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollEngines())); + } + + pollTimer->start(); + } +} + +void QNetworkConfigurationManagerPrivate::pollEngines() +{ + QMutexLocker locker(&mutex); + + for (int i = 0; i < sessionEngines.count(); ++i) { + if ((forcedPolling && sessionEngines.at(i)->requiresPolling()) || + sessionEngines.at(i)->configurationsInUse()) { + pollingEngines.insert(i); + QMetaObject::invokeMethod(sessionEngines.at(i), "requestUpdate"); + } + } +} + +void QNetworkConfigurationManagerPrivate::enablePolling() +{ + QMutexLocker locker(&mutex); + + ++forcedPolling; + + if (forcedPolling == 1) + QMetaObject::invokeMethod(this, "startPolling"); +} + +void QNetworkConfigurationManagerPrivate::disablePolling() +{ + QMutexLocker locker(&mutex); + + --forcedPolling; +} + +QT_END_NAMESPACE diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h new file mode 100644 index 0000000..dba9d2c --- /dev/null +++ b/src/network/bearer/qnetworkconfigmanager_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKCONFIGURATIONMANAGERPRIVATE_H +#define QNETWORKCONFIGURATIONMANAGERPRIVATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkconfigmanager.h" +#include "qnetworkconfiguration_p.h" + +#include <QtCore/qmutex.h> +#include <QtCore/qset.h> + +QT_BEGIN_NAMESPACE + +class QBearerEngine; +class QTimer; + +class Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate : public QObject +{ + Q_OBJECT + +public: + QNetworkConfigurationManagerPrivate(); + virtual ~QNetworkConfigurationManagerPrivate(); + + QNetworkConfiguration defaultConfiguration(); + QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter); + QNetworkConfiguration configurationFromIdentifier(const QString &identifier); + + bool isOnline(); + + QNetworkConfigurationManager::Capabilities capFlags; + + void performAsyncConfigurationUpdate(); + + QList<QBearerEngine *> engines(); + + Q_INVOKABLE void startPolling(); + + void enablePolling(); + void disablePolling(); + +public slots: + void updateConfigurations(); + +Q_SIGNALS: + void configurationAdded(const QNetworkConfiguration& config); + void configurationRemoved(const QNetworkConfiguration& config); + void configurationUpdateComplete(); + void configurationChanged(const QNetworkConfiguration& config); + void onlineStateChanged(bool isOnline); + + void abort(); + +private: + QMutex mutex; + + QTimer *pollTimer; + + QList<QBearerEngine *> sessionEngines; + + QSet<QString> onlineConfigurations; + + QSet<int> updatingEngines; + bool updating; + + QSet<int> pollingEngines; + int forcedPolling; + + bool firstUpdate; + +private Q_SLOTS: + void configurationAdded(QNetworkConfigurationPrivatePointer ptr); + void configurationRemoved(QNetworkConfigurationPrivatePointer ptr); + void configurationChanged(QNetworkConfigurationPrivatePointer ptr); + + void pollEngines(); +}; + +Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate(); + +QT_END_NAMESPACE + +#endif //QNETWORKCONFIGURATIONMANAGERPRIVATE_H diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp new file mode 100644 index 0000000..4108fee --- /dev/null +++ b/src/network/bearer/qnetworkconfiguration.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkconfiguration.h" +#include "qnetworkconfiguration_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QNetworkConfiguration + + \brief The QNetworkConfiguration class provides an abstraction of one or more access point configurations. + + \since 4.7 + + \inmodule QtNetwork + \ingroup network + + QNetworkConfiguration encapsulates a single access point or service network. + In most cases a single access point configuration can be mapped to one network + interface. However a single network interface may not always map to only one + access point configuration. Multiple configurations for the same + network device may enable multiple access points. An example + device that could exhibit such a configuration might be a + Smartphone which allows the user to manage multiple WLAN + configurations while the device itself has only one WLAN network device. + + The QNetworkConfiguration also supports the concept of service networks. + This concept allows the grouping of multiple access point configurations + into one entity. Such a group is called service network and can be + beneficial in cases whereby a network session to a + particular destination network is required (e.g. a company network). + When using a service network the user doesn't usually care which one of the + connectivity options is chosen (e.g. corporate WLAN or VPN via GPRS) + as long as he can reach the company's target server. Depending + on the current position and time some of the access points that make + up the service network may not even be available. Furthermore + automated access point roaming can be enabled which enables the device + to change the network interface configuration dynamically while maintaining + the applications connection to the target network. It allows adaption + to the changing environment and may enable optimization with regards to + cost, speed or other network parameters. + + Special configurations of type UserChoice provide a placeholder configuration which is + resolved to an actual network configuration by the platform when a + \l {QNetworkSession}{session} is \l {QNetworkSession::open()}{opened}. Not all platforms + support the concept of a user choice configuration. + + \section1 Configuration states + + The list of available configurations can be obtained via + QNetworkConfigurationManager::allConfigurations(). A configuration can have + multiple states. The \l Defined configuration state indicates that the configuration + is stored on the device. However the configuration is not yet ready to be activated + as e.g. a WLAN may not be available at the current time. + + The \l Discovered state implies that the configuration is \l Defined and + the outside conditions are such that the configuration can be used immediately + to open a new network session. An example of such an outside condition may be + that the Ethernet cable is actually connected to the device or that the WLAN + with the specified SSID is in range. + + The \l Active state implies that the configuration is \l Discovered. A configuration + in this state is currently being used by an application. The underlying network + interface has a valid IP configuration and can transfer IP packets between the + device and the target network. + + The \l Undefined state indicates that the system has knowledge of possible target + networks but cannot actually use that knowledge to connect to it. An example + for such a state could be an encrypted WLAN that has been discovered + but the user hasn't actually saved a configuration including the required password + which would allow the device to connect to it. + + Depending on the type of configuration some states are transient in nature. A GPRS/UMTS + connection may almost always be \l Discovered if the GSM/UMTS network is available. + However if the GSM/UMTS network looses the connection the associated configuration may change its state + from \l Discovered to \l Defined as well. A similar use case might be triggered by + WLAN availability. QNetworkConfigurationManager::updateConfigurations() can be used to + manually trigger updates of states. Note that some platforms do not require such updates + as they implicitly change the state once it has been discovered. If the state of a + configuration changes all related QNetworkConfiguration instances change their state automatically. + + \sa QNetworkSession, QNetworkConfigurationManager +*/ + +/*! + \enum QNetworkConfiguration::Type + + This enum describes the type of configuration. + + \value InternetAccessPoint The configuration specifies the details for a single access point. + Note that configurations of type InternetAccessPoint may be part + of other QNetworkConfigurations of type ServiceNetwork. + \value ServiceNetwork The configuration is based on a group of QNetworkConfigurations of + type InternetAccessPoint. All group members can reach the same + target network. This type of configuration is a mandatory + requirement for roaming enabled network sessions. On some + platforms this form of configuration may also be called Service + Network Access Point (SNAP). + \value UserChoice The configuration is a placeholder which will be resolved to an + actual configuration by the platform when a session is opened. Depending + on the platform the selection may generate a popup dialog asking the user + for his preferred choice. + \value Invalid The configuration is invalid. +*/ + +/*! + \enum QNetworkConfiguration::StateFlag + + Specifies the configuration states. + + \value Undefined This state is used for transient configurations such as newly discovered + WLANs for which the user has not actually created a configuration yet. + \value Defined Defined configurations are known to the system but are not immediately + usable (e.g. a configured WLAN is not within range or the Ethernet cable + is currently not plugged into the machine). + \value Discovered A discovered configuration can be immediately used to create a new + QNetworkSession. An example of a discovered configuration could be a WLAN + which is within in range. If the device moves out of range the discovered + flag is dropped. A second example is a GPRS configuration which generally + remains discovered for as long as the device has network coverage. A + configuration that has this state is also in state + QNetworkConfiguration::Defined. If the configuration is a service network + this flag is set if at least one of the underlying access points + configurations has the Discovered state. + \value Active The configuration is currently used by an open network session + (see \l QNetworkSession::isOpen()). However this does not mean that the + current process is the entity that created the open session. It merely + indicates that if a new QNetworkSession were to be constructed based on + this configuration \l QNetworkSession::state() would return + \l QNetworkSession::Connected. This state implies the + QNetworkConfiguration::Discovered state. +*/ + +/*! + \enum QNetworkConfiguration::Purpose + + Specifies the purpose of the configuration. + + \value UnknownPurpose The configuration doesn't specify any purpose. This is the default value. + \value PublicPurpose The configuration can be used for general purpose internet access. + \value PrivatePurpose The configuration is suitable to access a private network such as an office Intranet. + \value ServiceSpecificPurpose The configuration can be used for operator specific services (e.g. + receiving MMS messages or content streaming). +*/ + +/*! + Constructs an invalid configuration object. + + \sa isValid() +*/ +QNetworkConfiguration::QNetworkConfiguration() + : d(0) +{ +} + +/*! + Creates a copy of the QNetworkConfiguration object contained in \a other. +*/ +QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration& other) + : d(other.d) +{ +} + +/*! + Copies the content of the QNetworkConfiguration object contained in \a other into this one. +*/ +QNetworkConfiguration& QNetworkConfiguration::operator=(const QNetworkConfiguration& other) +{ + d = other.d; + return *this; +} + +/*! + Frees the resources associated with the QNetworkConfiguration object. +*/ +QNetworkConfiguration::~QNetworkConfiguration() +{ +} + +/*! + Returns true, if this configuration is the same as the \a other + configuration given; otherwise returns false. +*/ +bool QNetworkConfiguration::operator==(const QNetworkConfiguration& other) const +{ + if (!d) + return !other.d; + + if (!other.d) + return false; + + return (d == other.d); +} + +/*! + \fn bool QNetworkConfiguration::operator!=(const QNetworkConfiguration& other) const + + Returns true if this configuration is not the same as the \a other + configuration given; otherwise returns false. +*/ + +/*! + Returns the user visible name of this configuration. + + The name may either be the name of the underlying access point or the + name for service network that this configuration represents. +*/ +QString QNetworkConfiguration::name() const +{ + if (!d) + return QString(); + + QMutexLocker locker(&d->mutex); + return d->name; +} + +/*! + Returns the unique and platform specific identifier for this network configuration; + otherwise an empty string. +*/ +QString QNetworkConfiguration::identifier() const +{ + if (!d) + return QString(); + + QMutexLocker locker(&d->mutex); + return d->id; +} + +/*! + Returns the type of the configuration. + + A configuration can represent a single access point configuration or + a set of access point configurations. Such a set is called service network. + A configuration that is based on a service network can potentially support + roaming of network sessions. +*/ +QNetworkConfiguration::Type QNetworkConfiguration::type() const +{ + if (!d) + return QNetworkConfiguration::Invalid; + + QMutexLocker locker(&d->mutex); + return d->type; +} + +/*! + Returns true if this QNetworkConfiguration object is valid. + A configuration may become invalid if the user deletes the configuration or + the configuration was default-constructed. + + The addition and removal of configurations can be monitored via the + QNetworkConfigurationManager. + + \sa QNetworkConfigurationManager +*/ +bool QNetworkConfiguration::isValid() const +{ + if (!d) + return false; + + QMutexLocker locker(&d->mutex); + return d->isValid; +} + +/*! + Returns the current state of the configuration. +*/ +QNetworkConfiguration::StateFlags QNetworkConfiguration::state() const +{ + if (!d) + return QNetworkConfiguration::Undefined; + + QMutexLocker locker(&d->mutex); + return d->state; +} + +/*! + Returns the purpose of this configuration. + + The purpose field may be used to programmatically determine the + purpose of a configuration. Such information is usually part of the + access point or service network meta data. +*/ +QNetworkConfiguration::Purpose QNetworkConfiguration::purpose() const +{ + if (!d) + return QNetworkConfiguration::UnknownPurpose; + + QMutexLocker locker(&d->mutex); + return d->purpose; +} + +/*! + Returns true if this configuration supports roaming; otherwise false. +*/ +bool QNetworkConfiguration::isRoamingAvailable() const +{ + if (!d) + return false; + + QMutexLocker locker(&d->mutex); + return d->roamingSupported; +} + +/*! + Returns all sub configurations of this network configuration. + Only network configurations of type \l ServiceNetwork can have children. Otherwise + this function returns an empty list. +*/ +QList<QNetworkConfiguration> QNetworkConfiguration::children() const +{ + QList<QNetworkConfiguration> results; + + if (type() != QNetworkConfiguration::ServiceNetwork || !isValid()) + return results; + + QMutexLocker locker(&d->mutex); + + QMutableListIterator<QNetworkConfigurationPrivatePointer> iter(d->serviceNetworkMembers); + while (iter.hasNext()) { + QNetworkConfigurationPrivatePointer p = iter.next(); + + //if we have an invalid member get rid of it -> was deleted earlier on + { + QMutexLocker childLocker(&p->mutex); + + if (!p->isValid) { + iter.remove(); + continue; + } + } + + QNetworkConfiguration item; + item.d = p; + results << item; + } + + return results; +} + +/*! + Returns the type of bearer. The string is not translated and + therefore can not be shown to the user. The subsequent table presents the currently known + bearer types: + + \table + \header + \o Value + \o Description + \row + \o Unknown + \o The session is based on an unknown or unspecified bearer type. + \row + \o Ethernet + \o The session is based on Ethernet. + \row + \o WLAN + \o The session is based on Wireless LAN. + \row + \o 2G + \o The session uses CSD, GPRS, HSCSD, EDGE or cdmaOne. + \row + \o CDMA2000 + \o The session uses CDMA. + \row + \o WCDMA + \o The session uses W-CDMA/UMTS. + \row + \o HSPA + \o The session uses High Speed Packet Access. + \row + \o Bluetooth + \o The session uses Bluetooth. + \row + \o WiMAX + \o The session uses WiMAX. + \endtable + + This function returns an empty string if this is an invalid configuration, + a network configuration of type \l QNetworkConfiguration::ServiceNetwork or + \l QNetworkConfiguration::UserChoice. +*/ +QString QNetworkConfiguration::bearerName() const +{ + if (!isValid()) + return QString(); + + return d->bearerName(); +} + + +QT_END_NAMESPACE + diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h new file mode 100644 index 0000000..dad6198 --- /dev/null +++ b/src/network/bearer/qnetworkconfiguration.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKCONFIGURATION_H +#define QNETWORKCONFIGURATION_H + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qstring.h> +#include <QtCore/qlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QNetworkConfigurationPrivate; +class Q_NETWORK_EXPORT QNetworkConfiguration +{ +public: + QNetworkConfiguration(); + QNetworkConfiguration(const QNetworkConfiguration& other); + QNetworkConfiguration &operator=(const QNetworkConfiguration& other); + ~QNetworkConfiguration(); + + bool operator==(const QNetworkConfiguration& cp) const; + inline bool operator!=(const QNetworkConfiguration& cp) const + { return !operator==(cp); } + + enum Type { + InternetAccessPoint = 0, + ServiceNetwork, + UserChoice, + Invalid + }; + + enum Purpose { + UnknownPurpose = 0, + PublicPurpose, + PrivatePurpose, + ServiceSpecificPurpose + }; + + enum StateFlag { + Undefined = 0x0000001, + Defined = 0x0000002, + Discovered = 0x0000006, + Active = 0x000000e + }; + + Q_DECLARE_FLAGS(StateFlags, StateFlag) + + StateFlags state() const; + Type type() const; + Purpose purpose() const; + QString bearerName() const; + QString identifier() const; + bool isRoamingAvailable() const; + QList<QNetworkConfiguration> children() const; + + QString name() const; + bool isValid() const; + +private: + friend class QNetworkConfigurationPrivate; + friend class QNetworkConfigurationManager; + friend class QNetworkConfigurationManagerPrivate; + friend class QNetworkSessionPrivate; + QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QNETWORKCONFIGURATION_H diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h new file mode 100644 index 0000000..ccbe670 --- /dev/null +++ b/src/network/bearer/qnetworkconfiguration_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKCONFIGURATIONPRIVATE_H +#define QNETWORKCONFIGURATIONPRIVATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkconfiguration.h" + +#include <QtCore/qshareddata.h> +#include <QtCore/qmutex.h> + +QT_BEGIN_NAMESPACE + +typedef QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfigurationPrivatePointer; +class QNetworkConfigurationPrivate : public QSharedData +{ +public: + QNetworkConfigurationPrivate () + : mutex(QMutex::Recursive), type(QNetworkConfiguration::Invalid), + purpose(QNetworkConfiguration::UnknownPurpose), + isValid(false), roamingSupported(false) + { + } + + virtual ~QNetworkConfigurationPrivate() + { + //release pointers to member configurations + serviceNetworkMembers.clear(); + } + + virtual QString bearerName() const + { + QMutexLocker locker(&mutex); + + return bearer; + } + + mutable QMutex mutex; + + QString bearer; + QString name; + QString id; + + QNetworkConfiguration::StateFlags state; + QNetworkConfiguration::Type type; + QNetworkConfiguration::Purpose purpose; + + QList<QNetworkConfigurationPrivatePointer> serviceNetworkMembers; + + bool isValid; + bool roamingSupported; + +private: + // disallow detaching + QNetworkConfigurationPrivate &operator=(const QNetworkConfigurationPrivate &other); + QNetworkConfigurationPrivate(const QNetworkConfigurationPrivate &other); +}; + +QT_END_NAMESPACE + +#endif //QNETWORKCONFIGURATIONPRIVATE_H diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp new file mode 100644 index 0000000..1bba56f --- /dev/null +++ b/src/network/bearer/qnetworksession.cpp @@ -0,0 +1,704 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QEventLoop> +#include <QTimer> + +#include "qnetworksession.h" +#include "qbearerengine_p.h" +#include "qnetworkconfigmanager_p.h" +#include "qnetworksession_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QNetworkSession + + \brief The QNetworkSession class provides control over the system's access points + and enables session management for cases when multiple clients access the same access point. + + \since 4.7 + + \inmodule QtNetwork + \ingroup network + + A QNetworkSession enables control over the system's network interfaces. The session's configuration + parameter are determined via the QNetworkConfiguration object to which it is bound. Depending on the + type of the session (single access point or service network) a session may be linked to one or more + network interfaces. By means of \l{open()}{opening} and \l{close()}{closing} of network sessions + a developer can start and stop the systems network interfaces. If the configuration represents + multiple access points (see \l QNetworkConfiguration::ServiceNetwork) more advanced features such as roaming may be supported. + + QNetworkSession supports session management within the same process and depending on the platform's + capabilities may support out-of-process sessions. If the same + network configuration is used by multiple open sessions the underlying network interface is only terminated once + the last session has been closed. + + \section1 Roaming + + Applications may connect to the preferredConfigurationChanged() signal in order to + receive notifications when a more suitable access point becomes available. + In response to this signal the application must either initiate the roaming via migrate() + or ignore() the new access point. Once the session has roamed the + newConfigurationActivated() signal is emitted. The application may now test the + carrier and must either accept() or reject() it. The session will return to the previous + access point if the roaming was rejected. The subsequent state diagram depicts the required + state transitions. + + \image roaming-states.png + + Some platforms may distinguish forced roaming and application level roaming (ALR). + ALR implies that the application controls (via migrate(), ignore(), accept() and reject()) + whether a network session can roam from one access point to the next. Such control is useful + if the application maintains stateful socket connections and wants to control the transition from + one interface to the next. Forced roaming implies that the system automatically roams to the next network without + consulting the application. This has the advantage that the application can make use of roaming features + without actually being aware of it. It is expected that the application detects that the underlying + socket is broken and automatically reconnects via the new network link. + + If the platform supports both modes of roaming, an application indicates its preference + by connecting to the preferredConfigurationChanged() signal. Connecting to this signal means that + the application wants to take control over the roaming behavior and therefore implies application + level roaming. If the client does not connect to the preferredConfigurationChanged(), forced roaming + is used. If forced roaming is not supported the network session will not roam by default. + + Some applications may want to suppress any form of roaming altogether. Possible use cases may be + high priority downloads or remote services which cannot handle a roaming enabled client. Clients + can suppress roaming by connecting to the preferredConfigurationChanged() signal and answer each + signal emission with ignore(). + + \sa QNetworkConfiguration, QNetworkConfigurationManager +*/ + +/*! + \enum QNetworkSession::State + + This enum describes the connectivity state of the session. If the session is based on a + single access point configuration the state of the session is the same as the state of the + associated network interface. + + \value Invalid The session is invalid due to an invalid configuration. This may + happen due to a removed access point or a configuration that was + invalid to begin with. + \value NotAvailable The session is based on a defined but not yet discovered QNetworkConfiguration + (see \l QNetworkConfiguration::StateFlag). + \value Connecting The network session is being established. + \value Connected The network session is connected. If the current process wishes to use this session + it has to register its interest by calling open(). A network session + is considered to be ready for socket operations if it isOpen() and connected. + \value Closing The network session is in the process of being shut down. + \value Disconnected The network session is not connected. The associated QNetworkConfiguration + has the state QNetworkConfiguration::Discovered. + \value Roaming The network session is roaming from one access point to another + access point. +*/ + +/*! + \enum QNetworkSession::SessionError + + This enum describes the session errors that can occur. + + \value UnknownSessionError An unidentified error occurred. + \value SessionAbortedError The session was aborted by the user or system. + \value RoamingError The session cannot roam to a new configuration. + \value OperationNotSupportedError The operation is not supported for current configuration. + \value InvalidConfigurationError The operation cannot currently be performed for the + current configuration. +*/ + +/*! + \fn void QNetworkSession::stateChanged(QNetworkSession::State state) + + This signal is emitted whenever the state of the network session changes. + The \a state parameter is the new state. + + \sa state() +*/ + +/*! + \fn void QNetworkSession::error(QNetworkSession::SessionError error) + + This signal is emitted after an error occurred. The \a error parameter + describes the error that occurred. + + \sa error(), errorString() +*/ + +/*! + \fn void QNetworkSession::preferredConfigurationChanged(const QNetworkConfiguration& config, bool isSeamless) + + This signal is emitted when the preferred configuration/access point for the + session changes. Only sessions which are based on service network configurations + may emit this signal. \a config can be used to determine access point specific + details such as proxy settings and \a isSeamless indicates whether roaming will + break the sessions IP address. + + As a consequence to this signal the application must either start the roaming process + by calling migrate() or choose to ignore() the new access point. + + If the roaming process is non-seamless the IP address will change which means that + a socket becomes invalid. However seamless mobility can ensure that the local IP address + does not change. This is achieved by using a virtual IP address which is bound to the actual + link address. During the roaming process the virtual address is attached to the new link + address. + + Some platforms may support the concept of Forced Roaming and Application Level Roaming (ALR). + Forced roaming implies that the platform may simply roam to a new configuration without + consulting applications. It is up to the application to detect the link layer loss and reestablish + its sockets. In contrast ALR provides the opportunity to prevent the system from roaming. + If this session is based on a configuration that supports roaming the application can choose + whether it wants to be consulted (ALR use case) by connecting to this signal. For as long as this signal + connection remains the session remains registered as a roaming stakeholder; otherwise roaming will + be enforced by the platform. + + \sa migrate(), ignore(), QNetworkConfiguration::isRoamingAvailable() +*/ + +/*! + \fn void QNetworkSession::newConfigurationActivated() + + This signal is emitted once the session has roamed to the new access point. + The application may reopen its socket and test the suitability of the new network link. + Subsequently it must either accept() or reject() the new access point. + + \sa accept(), reject() +*/ + +/*! + \fn void QNetworkSession::opened() + + This signal is emitted when the network session has been opened. + + The underlying network interface will not be shut down as long as the session remains open. + Note that this feature is dependent on \l{QNetworkConfigurationManager::SystemSessionSupport}{system wide session support}. +*/ + +/*! + \fn void QNetworkSession::closed() + + This signal is emitted when the network session has been closed. +*/ + +/*! + Constructs a session based on \a connectionConfig with the given \a parent. + + \sa QNetworkConfiguration +*/ +QNetworkSession::QNetworkSession(const QNetworkConfiguration& connectionConfig, QObject* parent) +: QObject(parent), d(0) +{ + foreach (QBearerEngine *engine, qNetworkConfigurationManagerPrivate()->engines()) { + if (engine->hasIdentifier(connectionConfig.identifier())) { + d = engine->createSessionBackend(); + d->q = this; + d->publicConfig = connectionConfig; + d->syncStateWithInterface(); + connect(d, SIGNAL(quitPendingWaitsForOpened()), this, SIGNAL(opened())); + connect(d, SIGNAL(error(QNetworkSession::SessionError)), + this, SIGNAL(error(QNetworkSession::SessionError))); + connect(d, SIGNAL(stateChanged(QNetworkSession::State)), + this, SIGNAL(stateChanged(QNetworkSession::State))); + connect(d, SIGNAL(closed()), this, SIGNAL(closed())); + connect(d, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)), + this, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))); + connect(d, SIGNAL(newConfigurationActivated()), + this, SIGNAL(newConfigurationActivated())); + break; + } + } +} + +/*! + Frees the resources associated with the QNetworkSession object. +*/ +QNetworkSession::~QNetworkSession() +{ + delete d; +} + +/*! + Creates an open session which increases the session counter on the underlying network interface. + The system will not terminate a network interface until the session reference counter reaches zero. + Therefore an open session allows an application to register its use of the interface. + + As a result of calling open() the interface will be started if it is not connected/up yet. + Some platforms may not provide support for out-of-process sessions. On such platforms the session + counter ignores any sessions held by another process. The platform capabilities can be + detected via QNetworkConfigurationManager::capabilities(). + + Note that this call is asynchronous. Depending on the outcome of this call the results can be enquired + by connecting to the stateChanged(), opened() or error() signals. + + It is not a requirement to open a session in order to monitor the underlying network interface. + + \sa close(), stop(), isOpen() +*/ +void QNetworkSession::open() +{ + if (d) + d->open(); + else + emit error(InvalidConfigurationError); +} + +/*! + Waits until the session has been opened, up to \a msecs milliseconds. If the session has been opened, this + function returns true; otherwise it returns false. In the case where it returns false, you can call error() + to determine the cause of the error. + + The following example waits up to one second for the session to be opened: + + \code + session->open(); + if (session->waitForOpened(1000)) + qDebug("Open!"); + \endcode + + If \a msecs is -1, this function will not time out. + + \sa open(), error() +*/ +bool QNetworkSession::waitForOpened(int msecs) +{ + if (!d) + return false; + + if (d->isOpen) + return true; + + if (d->state != Connecting) + return false; + + QEventLoop* loop = new QEventLoop(this); + QObject::connect(d, SIGNAL(quitPendingWaitsForOpened()), + loop, SLOT(quit())); + QObject::connect(this, SIGNAL(error(QNetworkSession::SessionError)), + loop, SLOT(quit())); + + //final call + if (msecs>=0) + QTimer::singleShot(msecs, loop, SLOT(quit())); + + loop->exec(); + loop->disconnect(); + loop->deleteLater(); + + return d->isOpen; +} + +/*! + Decreases the session counter on the associated network configuration. If the session counter reaches zero + the active network interface is shut down. This also means that state() will only change from \l Connected to + \l Disconnected if the current session was the last open session. + + If the platform does not support out-of-process sessions calling this function does not stop the + interface. In this case \l{stop()} has to be used to force a shut down. + The platform capabilities can be detected via QNetworkConfigurationManager::capabilities(). + + Note that this call is asynchronous. Depending on the outcome of this call the results can be enquired + by connecting to the stateChanged(), opened() or error() signals. + + \sa open(), stop(), isOpen() +*/ +void QNetworkSession::close() +{ + if (d) + d->close(); +} + +/*! + Invalidates all open sessions against the network interface and therefore stops the + underlying network interface. This function always changes the session's state() flag to + \l Disconnected. + + On Symbian platform, a 'NetworkControl' capability is required for + full interface-level stop (without the capability, only the current session is stopped). + + \sa open(), close() +*/ +void QNetworkSession::stop() +{ + if (d) + d->stop(); +} + +/*! + Returns the QNetworkConfiguration that this network session object is based on. + + \sa QNetworkConfiguration +*/ +QNetworkConfiguration QNetworkSession::configuration() const +{ + return d ? d->publicConfig : QNetworkConfiguration(); +} + +#ifndef QT_NO_NETWORKINTERFACE +/*! + Returns the network interface that is used by this session. + + This function only returns a valid QNetworkInterface when this session is \l Connected. + + The returned interface may change as a result of a roaming process. + + Note: this function does not work in Symbian emulator due to the way the + connectivity is emulated on Windows. + + \sa state() +*/ +QNetworkInterface QNetworkSession::interface() const +{ + return d ? d->currentInterface() : QNetworkInterface(); +} +#endif + +/*! + Returns true if this session is open. If the number of all open sessions is greater than + zero the underlying network interface will remain connected/up. + + The session can be controlled via open() and close(). +*/ +bool QNetworkSession::isOpen() const +{ + return d ? d->isOpen : false; +} + +/*! + Returns the state of the session. + + If the session is based on a single access point configuration the state of the + session is the same as the state of the associated network interface. Therefore + a network session object can be used to monitor network interfaces. + + A \l QNetworkConfiguration::ServiceNetwork based session summarizes the state of all its children + and therefore returns the \l Connected state if at least one of the service network's + \l {QNetworkConfiguration::children()}{children()} configurations is active. + + Note that it is not required to hold an open session in order to obtain the network interface state. + A connected but closed session may be used to monitor network interfaces whereas an open and connected + session object may prevent the network interface from being shut down. + + \sa error(), stateChanged() +*/ +QNetworkSession::State QNetworkSession::state() const +{ + return d ? d->state : QNetworkSession::Invalid; +} + +/*! + Returns the type of error that last occurred. + + \sa state(), errorString() +*/ +QNetworkSession::SessionError QNetworkSession::error() const +{ + return d ? d->error() : InvalidConfigurationError; +} + +/*! + Returns a human-readable description of the last device error that + occurred. + + \sa error() +*/ +QString QNetworkSession::errorString() const +{ + return d ? d->errorString() : tr("Invalid configuration."); +} + +/*! + Returns the value for property \a key. + + A network session can have properties attached which may describe the session in more details. + This function can be used to gain access to those properties. + + The following property keys are guaranteed to be specified on all platforms: + + \table + \header + \o Key \o Description + \row + \o ActiveConfiguration + \o If the session \l isOpen() this property returns the identifier of the + QNetworkConfiguration that is used by this session; otherwise an empty string. + + The main purpose of this key is to determine which Internet access point is used + if the session is based on a \l{QNetworkConfiguration::ServiceNetwork}{ServiceNetwork}. + The following code snippet highlights the difference: + \code + QNetworkConfigurationManager mgr; + QNetworkConfiguration ap = mgr.defaultConfiguration(); + QNetworkSession* session = new QNetworkSession(ap); + ... //code activates session + + QString ident = session->sessionProperty("ActiveConfiguration").toString(); + if ( ap.type() == QNetworkConfiguration::ServiceNetwork ) { + Q_ASSERT( ap.identifier() != ident ); + Q_ASSERT( ap.children().contains( mgr.configurationFromIdentifier(ident) ) ); + } else if ( ap.type() == QNetworkConfiguration::InternetAccessPoint ) { + Q_ASSERT( ap.identifier() == ident ); + } + \endcode + \row + \o UserChoiceConfiguration + \o If the session \l isOpen() and is bound to a QNetworkConfiguration of type + UserChoice, this property returns the identifier of the QNetworkConfiguration that the + configuration resolved to when \l open() was called; otherwise an empty string. + + The purpose of this key is to determine the real QNetworkConfiguration that the + session is using. This key is different to \i ActiveConfiguration in that + this key may return an identifier for either a + \l {QNetworkConfiguration::ServiceNetwork}{service network} or a + \l {QNetworkConfiguration::InternetAccessPoint}{Internet access points} configurations + whereas \i ActiveConfiguration always returns identifiers to + \l {QNetworkConfiguration::InternetAccessPoint}{Internet access points} configurations. + \row + \o ConnectInBackground + \o Setting this property to \i true before calling \l open() implies that the connection attempt + is made but if no connection can be established, the user is not connsulted and asked to select + a suitable connection. This property is not set by default and support for it depends on the platform. + + \row + \o AutoCloseSessionTimeout + \o If the session requires polling to keep its state up to date, this property holds + the timeout in milliseconds before the session will automatically close. If the + value of this property is -1 the session will not automatically close. This property + is set to -1 by default. + + The purpose of this property is to minimize resource use on platforms that use + polling to update the state of the session. Applications can set the value of this + property to the desired timeout before the session is closed. In response to the + closed() signal the network session should be deleted to ensure that all polling is + stopped. The session can then be recreated once it is required again. This property + has no effect for sessions that do not require polling. + \endtable +*/ +QVariant QNetworkSession::sessionProperty(const QString& key) const +{ + if (!d) + return QVariant(); + + if (!d->publicConfig.isValid()) + return QVariant(); + + if (key == QLatin1String("ActiveConfiguration")) { + if (!d->isOpen) + return QString(); + else + return d->activeConfig.identifier(); + } + + if (key == QLatin1String("UserChoiceConfiguration")) { + if (!d->isOpen || d->publicConfig.type() != QNetworkConfiguration::UserChoice) + return QString(); + + if (d->serviceConfig.isValid()) + return d->serviceConfig.identifier(); + else + return d->activeConfig.identifier(); + } + + return d->sessionProperty(key); +} + +/*! + Sets the property \a value on the session. The property is identified using + \a key. Removing an already set property can be achieved by passing an + invalid QVariant. + + Note that the \e UserChoiceConfiguration and \e ActiveConfiguration + properties are read only and cannot be changed using this method. +*/ +void QNetworkSession::setSessionProperty(const QString& key, const QVariant& value) +{ + if (!d) + return; + + if (key == QLatin1String("ActiveConfiguration") || + key == QLatin1String("UserChoiceConfiguration")) { + return; + } + + d->setSessionProperty(key, value); +} + +/*! + Instructs the session to roam to the new access point. The old access point remains active + until the application calls accept(). + + The newConfigurationActivated() signal is emitted once roaming has been completed. + + \sa accept() +*/ +void QNetworkSession::migrate() +{ + if (d) + d->migrate(); +} + +/*! + This function indicates that the application does not wish to roam the session. + + \sa migrate() +*/ +void QNetworkSession::ignore() +{ + // Needed on at least Symbian platform: the roaming must be explicitly + // ignore()'d or migrate()'d + if (d) + d->ignore(); +} + +/*! + Instructs the session to permanently accept the new access point. Once this function + has been called the session may not return to the old access point. + + The old access point may be closed in the process if there are no other network sessions for it. + Therefore any open socket that still uses the old access point + may become unusable and should be closed before completing the migration. +*/ +void QNetworkSession::accept() +{ + if (d) + d->accept(); +} + +/*! + The new access point is not suitable for the application. By calling this function the + session returns to the previous access point/configuration. This action may invalidate + any socket that has been created via the not desired access point. + + \sa accept() +*/ +void QNetworkSession::reject() +{ + if (d) + d->reject(); +} + + +/*! + Returns the amount of data sent in bytes; otherwise 0. + + This field value includes the usage across all open network + sessions which use the same network interface. + + If the session is based on a service network configuration the number of + sent bytes across all active member configurations are returned. + + This function may not always be supported on all platforms and returns + 0. The platform capability can be detected via QNetworkConfigurationManager::DataStatistics. +*/ +quint64 QNetworkSession::bytesWritten() const +{ + return d ? d->bytesWritten() : Q_UINT64_C(0); +} + +/*! + Returns the amount of data received in bytes; otherwise 0. + + This field value includes the usage across all open network + sessions which use the same network interface. + + If the session is based on a service network configuration the number of + sent bytes across all active member configurations are returned. + + This function may not always be supported on all platforms and returns + 0. The platform capability can be detected via QNetworkConfigurationManager::DataStatistics. +*/ +quint64 QNetworkSession::bytesReceived() const +{ + return d ? d->bytesReceived() : Q_UINT64_C(0); +} + +/*! + Returns the number of seconds that the session has been active. +*/ +quint64 QNetworkSession::activeTime() const +{ + return d ? d->activeTime() : Q_UINT64_C(0); +} + +/*! + \internal + + This function is required to detect whether the client wants to control + the roaming process. If he connects to preferredConfigurationChanged() signal + he intends to influence it. Otherwise QNetworkSession always roams + without registering this session as a stakeholder in the roaming process. + + For more details check the Forced vs ALR roaming section in the QNetworkSession + class description. +*/ +void QNetworkSession::connectNotify(const char *signal) +{ + QObject::connectNotify(signal); + //check for preferredConfigurationChanged() signal connect notification + //This is not required on all platforms + if (!d) + return; + + if (qstrcmp(signal, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) == 0) + d->setALREnabled(true); +} + +/*! + \internal + + This function is called when the client disconnects from the preferredConfigurationChanged() + signal. + + \sa connectNotify() +*/ +void QNetworkSession::disconnectNotify(const char *signal) +{ + QObject::disconnectNotify(signal); + //check for preferredConfigurationChanged() signal disconnect notification + //This is not required on all platforms + if (!d) + return; + + if (qstrcmp(signal, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) == 0) + d->setALREnabled(false); +} + +#include "moc_qnetworksession.cpp" + +QT_END_NAMESPACE diff --git a/src/network/bearer/qnetworksession.h b/src/network/bearer/qnetworksession.h new file mode 100644 index 0000000..596f527 --- /dev/null +++ b/src/network/bearer/qnetworksession.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKSESSION_H +#define QNETWORKSESSION_H + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtNetwork/qnetworkinterface.h> +#include <QtCore/qvariant.h> +#include <QtNetwork/qnetworkconfiguration.h> + +#if defined(Q_OS_WIN) && defined(interface) +#undef interface +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +class QNetworkSessionPrivate; +class Q_NETWORK_EXPORT QNetworkSession : public QObject +{ + Q_OBJECT +public: + enum State { + Invalid = 0, + NotAvailable, + Connecting, + Connected, + Closing, + Disconnected, + Roaming + }; + + enum SessionError { + UnknownSessionError = 0, + SessionAbortedError, + RoamingError, + OperationNotSupportedError, + InvalidConfigurationError + }; + + explicit QNetworkSession(const QNetworkConfiguration& connConfig, QObject* parent =0); + virtual ~QNetworkSession(); + + bool isOpen() const; + QNetworkConfiguration configuration() const; +#ifndef QT_NO_NETWORKINTERFACE + QNetworkInterface interface() const; +#endif + + State state() const; + SessionError error() const; + QString errorString() const; + QVariant sessionProperty(const QString& key) const; + void setSessionProperty(const QString& key, const QVariant& value); + + quint64 bytesWritten() const; + quint64 bytesReceived() const; + quint64 activeTime() const; + + bool waitForOpened(int msecs = 30000); + +public Q_SLOTS: + void open(); + void close(); + void stop(); + + //roaming related slots + void migrate(); + void ignore(); + void accept(); + void reject(); + + +Q_SIGNALS: + void stateChanged(QNetworkSession::State); + void opened(); + void closed(); + void error(QNetworkSession::SessionError); + void preferredConfigurationChanged(const QNetworkConfiguration& config, bool isSeamless); + void newConfigurationActivated(); + +protected: + virtual void connectNotify(const char *signal); + virtual void disconnectNotify(const char *signal); + +private: + QNetworkSessionPrivate* d; + friend class QNetworkSessionPrivate; + }; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QNETWORKSESSION_H diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h new file mode 100644 index 0000000..a341eaf --- /dev/null +++ b/src/network/bearer/qnetworksession_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKSESSIONPRIVATE_H +#define QNETWORKSESSIONPRIVATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworksession.h" +#include "qnetworkconfiguration_p.h" + +QT_BEGIN_NAMESPACE + +class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject +{ + Q_OBJECT + + friend class QNetworkSession; + +public: + QNetworkSessionPrivate() + : state(QNetworkSession::Invalid), isOpen(false) + { + } + + virtual ~QNetworkSessionPrivate() + { + } + + //called by QNetworkSession constructor and ensures + //that the state is immediately updated (w/o actually opening + //a session). Also this function should take care of + //notification hooks to discover future state changes. + virtual void syncStateWithInterface() = 0; + +#ifndef QT_NO_NETWORKINTERFACE + virtual QNetworkInterface currentInterface() const = 0; +#endif + virtual QVariant sessionProperty(const QString& key) const = 0; + virtual void setSessionProperty(const QString& key, const QVariant& value) = 0; + + virtual void open() = 0; + virtual void close() = 0; + virtual void stop() = 0; + + virtual void setALREnabled(bool /*enabled*/) { } + virtual void migrate() = 0; + virtual void accept() = 0; + virtual void ignore() = 0; + virtual void reject() = 0; + + virtual QString errorString() const = 0; //must return translated string + virtual QNetworkSession::SessionError error() const = 0; + + virtual quint64 bytesWritten() const = 0; + virtual quint64 bytesReceived() const = 0; + virtual quint64 activeTime() const = 0; + +protected: + inline QNetworkConfigurationPrivatePointer privateConfiguration(const QNetworkConfiguration &config) const + { + return config.d; + } + + inline void setPrivateConfiguration(QNetworkConfiguration &config, + QNetworkConfigurationPrivatePointer ptr) const + { + config.d = ptr; + } + +Q_SIGNALS: + //releases any pending waitForOpened() calls + void quitPendingWaitsForOpened(); + + void error(QNetworkSession::SessionError error); + void stateChanged(QNetworkSession::State state); + void closed(); + void newConfigurationActivated(); + void preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless); + +protected: + // The config set on QNetworkSession. + QNetworkConfiguration publicConfig; + + // If publicConfig is a ServiceNetwork this is a copy of publicConfig. + // If publicConfig is an UserChoice that is resolved to a ServiceNetwork this is the actual + // ServiceNetwork configuration. + QNetworkConfiguration serviceConfig; + + // This is the actual active configuration currently in use by the session. + // Either a copy of publicConfig or one of serviceConfig.children(). + QNetworkConfiguration activeConfig; + + QNetworkSession::State state; + bool isOpen; + + QNetworkSession *q; +}; + +QT_END_NAMESPACE + +#endif //QNETWORKSESSIONPRIVATE_H + diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 6894978..baf69e7 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -172,7 +172,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, if (name.isEmpty()) { QHostInfo hostInfo(id); hostInfo.setError(QHostInfo::HostNotFound); - hostInfo.setErrorString(QObject::tr("No host name given")); + hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given")); QScopedPointer<QHostInfoResult> result(new QHostInfoResult); QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index be06b6e..a186e78 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -193,7 +193,9 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName) results.setHostName(hostName); if (aceHostname.isEmpty()) { results.setError(QHostInfo::HostNotFound); - results.setErrorString(hostName.isEmpty() ? QObject::tr("No host name given") : QObject::tr("Invalid hostname")); + results.setErrorString(hostName.isEmpty() ? + QCoreApplication::translate("QHostInfoAgent", "No host name given") : + QCoreApplication::translate("QHostInfoAgent", "Invalid hostname")); return results; } diff --git a/src/network/network.pro b/src/network/network.pro index e890b94..8582d8a 100644 --- a/src/network/network.pro +++ b/src/network/network.pro @@ -17,6 +17,7 @@ unix:QMAKE_PKGCONFIG_REQUIRES = QtCore include(../qbase.pri) include(access/access.pri) +include(bearer/bearer.pri) include(kernel/kernel.pri) include(socket/socket.pri) include(ssl/ssl.pri) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 95721ee..21cd0fd 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -365,12 +365,12 @@ #include "private/qhostinfo_p.h" #include <qabstracteventdispatcher.h> -#include <qdatetime.h> #include <qhostaddress.h> #include <qhostinfo.h> #include <qmetaobject.h> #include <qpointer.h> #include <qtimer.h> +#include <qelapsedtimer.h> #ifndef QT_NO_OPENSSL #include <QtNetwork/qsslsocket.h> @@ -1738,7 +1738,7 @@ bool QAbstractSocket::waitForConnected(int msecs) bool wasPendingClose = d->pendingClose; d->pendingClose = false; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); if (d->state == HostLookupState) { @@ -1819,7 +1819,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs) return false; } - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // handle a socket in connecting state @@ -1878,7 +1878,7 @@ bool QAbstractSocket::waitForBytesWritten(int msecs) if (d->writeBuffer.isEmpty()) return false; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // handle a socket in connecting state @@ -1960,7 +1960,7 @@ bool QAbstractSocket::waitForDisconnected(int msecs) return false; } - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // handle a socket in connecting state diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index 3293ff5..dfda257 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -42,9 +42,9 @@ #include "qhttpsocketengine_p.h" #include "qtcpsocket.h" #include "qhostaddress.h" -#include "qdatetime.h" #include "qurl.h" #include "qhttp.h" +#include "qelapsedtimer.h" #if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP) #include <qdebug.h> @@ -319,7 +319,7 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut) if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState) return false; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // Wait for more data if nothing is available. @@ -366,7 +366,7 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut) return true; } - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // If we're not connected yet, wait until we are, and until bytes have diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp index 1ca11d8..f14decc 100644 --- a/src/network/socket/qlocalsocket_unix.cpp +++ b/src/network/socket/qlocalsocket_unix.cpp @@ -52,9 +52,9 @@ #include <fcntl.h> #include <errno.h> -#include <qdatetime.h> #include <qdir.h> #include <qdebug.h> +#include <qelapsedtimer.h> #ifdef Q_OS_VXWORKS # include <selectLib.h> @@ -534,7 +534,7 @@ bool QLocalSocket::waitForConnected(int msec) int result = -1; // on Linux timeout will be updated by select, but _not_ on other systems. - QTime timer; + QElapsedTimer timer; timer.start(); while (state() == ConnectingState && (-1 == msec || timer.elapsed() < msec)) { diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index a890b3b..a169ca0 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -185,6 +185,9 @@ void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, Er // socket to recreate its engine after an error. Note: There's // one exception: SocketError(11) bypasses this as it's purely // a temporary internal error condition. + // Another exception is the way the waitFor*() functions set + // an error when a timeout occurs. After the call to setError() + // they reset the hasSetSocketError to false return; } if (error != QAbstractSocket::SocketError(11)) @@ -859,6 +862,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut) *timedOut = true; d->setError(QAbstractSocket::SocketTimeoutError, QNativeSocketEnginePrivate::TimeOutErrorString); + d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions return false; } else if (state() == QAbstractSocket::ConnectingState) { connectToHost(d->peerAddress, d->peerPort); @@ -927,6 +931,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut) *timedOut = true; d->setError(QAbstractSocket::SocketTimeoutError, QNativeSocketEnginePrivate::TimeOutErrorString); + d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions return false; } else if (state() == QAbstractSocket::ConnectingState) { connectToHost(d->peerAddress, d->peerPort); @@ -978,6 +983,7 @@ bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWri *timedOut = true; d->setError(QAbstractSocket::SocketTimeoutError, QNativeSocketEnginePrivate::TimeOutErrorString); + d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions return false; } else if (state() == QAbstractSocket::ConnectingState) { connectToHost(d->peerAddress, d->peerPort); diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 9a2c349..6c800fe 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -44,8 +44,8 @@ #include "private/qnet_unix_p.h" #include "qiodevice.h" #include "qhostaddress.h" +#include "qelapsedtimer.h" #include "qvarlengtharray.h" -#include "qdatetime.h" #include <time.h> #include <errno.h> #include <fcntl.h> @@ -1003,7 +1003,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c #ifndef Q_OS_SYMBIAN ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); #else - QTime timer; + QElapsedTimer timer; timer.start(); do { diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 924530e..f68edfe 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -49,7 +49,7 @@ #include "qdebug.h" #include "qhash.h" #include "qqueue.h" -#include "qdatetime.h" +#include "qelapsedtimer.h" #include "qmutex.h" #include "qthread.h" #include "qcoreapplication.h" @@ -308,7 +308,7 @@ struct QSocks5BindData : public QSocks5Data quint16 localPort; QHostAddress peerAddress; quint16 peerPort; - QDateTime timeStamp; + QElapsedTimer timeStamp; }; struct QSocks5RevivedDatagram @@ -369,7 +369,7 @@ void QSocks5BindStore::add(int socketDescriptor, QSocks5BindData *bindData) if (store.contains(socketDescriptor)) { // qDebug() << "delete it"; } - bindData->timeStamp = QDateTime::currentDateTime(); + bindData->timeStamp.start(); store.insert(socketDescriptor, bindData); // start sweep timer if not started if (sweepTimerId == -1) @@ -412,7 +412,7 @@ void QSocks5BindStore::timerEvent(QTimerEvent * event) QMutableHashIterator<int, QSocks5BindData *> it(store); while (it.hasNext()) { it.next(); - if (it.value()->timeStamp.secsTo(QDateTime::currentDateTime()) > 350) { + if (it.value()->timeStamp.hasExpired(350000)) { QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ"; it.remove(); } @@ -1355,7 +1355,7 @@ bool QSocks5SocketEngine::bind(const QHostAddress &address, quint16 port) } int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port()); if (!d->waitForConnected(msecs, 0) || @@ -1455,7 +1455,7 @@ void QSocks5SocketEngine::close() if (d->data && d->data->controlSocket) { if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) { int msecs = 100; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); while (!d->data->controlSocket->bytesToWrite()) { if (!d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()))) @@ -1674,7 +1674,7 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut) mode == BindMode ? BindSuccess : UdpAssociateSuccess; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); while (socks5State != wantedState) { @@ -1699,7 +1699,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut) d->readNotificationActivated = false; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // are we connected yet? @@ -1749,7 +1749,7 @@ bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut) Q_D(QSocks5SocketEngine); QSOCKS5_DEBUG << "waitForWrite" << msecs; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); // are we connected yet? diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 9623570..86b11b9 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -286,8 +286,8 @@ #include <QtCore/qdebug.h> #include <QtCore/qdir.h> -#include <QtCore/qdatetime.h> #include <QtCore/qmutex.h> +#include <QtCore/qelapsedtimer.h> #include <QtNetwork/qhostaddress.h> #include <QtNetwork/qhostinfo.h> @@ -1393,7 +1393,7 @@ bool QSslSocket::waitForEncrypted(int msecs) if (d->mode == UnencryptedMode && !d->autoStartHandshake) return false; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); if (d->plainSocket->state() != QAbstractSocket::ConnectedState) { @@ -1433,7 +1433,7 @@ bool QSslSocket::waitForReadyRead(int msecs) bool *previousReadyReadEmittedPointer = d->readyReadEmittedPointer; d->readyReadEmittedPointer = &readyReadEmitted; - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); if (!d->connectionEncrypted) { @@ -1470,7 +1470,7 @@ bool QSslSocket::waitForBytesWritten(int msecs) if (d->mode == UnencryptedMode) return d->plainSocket->waitForBytesWritten(msecs); - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); if (!d->connectionEncrypted) { @@ -1508,7 +1508,7 @@ bool QSslSocket::waitForDisconnected(int msecs) if (d->mode == UnencryptedMode) return d->plainSocket->waitForDisconnected(msecs); - QTime stopWatch; + QElapsedTimer stopWatch; stopWatch.start(); if (!d->connectionEncrypted) { diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index ce2aee1..050fb1b 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -146,13 +146,14 @@ static void locking_function(int mode, int lockNumber, const char *, int) } static unsigned long id_function() { - return (unsigned long)QThread::currentThreadId(); + return (quintptr)QThread::currentThreadId(); } } // extern "C" QSslSocketBackendPrivate::QSslSocketBackendPrivate() : ssl(0), ctx(0), + pkey(0), readBio(0), writeBio(0), session(0) @@ -311,11 +312,14 @@ init_context: } // Load private key - EVP_PKEY *pkey = q_EVP_PKEY_new(); + pkey = q_EVP_PKEY_new(); + // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free. + // this lead to a memory leak. Now we use the *_set1_* functions which do not + // take ownership of the RSA/DSA key instance because the QSslKey already has ownership. if (configuration.privateKey.algorithm() == QSsl::Rsa) - q_EVP_PKEY_assign_RSA(pkey, (RSA *)configuration.privateKey.handle()); + q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle()); else - q_EVP_PKEY_assign_DSA(pkey, (DSA *)configuration.privateKey.handle()); + q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle()); if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) { q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(SSL_ERRORSTR())); emit q->error(QAbstractSocket::UnknownSocketError); @@ -922,6 +926,11 @@ void QSslSocketBackendPrivate::disconnected() q_SSL_CTX_free(ctx); ctx = 0; } + if (pkey) { + q_EVP_PKEY_free(pkey); + pkey = 0; + } + } QSslCipher QSslSocketBackendPrivate::sessionCipher() const diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 836f064..3c08757 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -97,6 +97,7 @@ public: bool initSslContext(); SSL *ssl; SSL_CTX *ctx; + EVP_PKEY *pkey; BIO *readBio; BIO *writeBio; SSL_SESSION *session; diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 94cc9d2..d2eb6f1 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -119,6 +119,8 @@ DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return) +DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return) +DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return) DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG) DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return 0, return) DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return 0, return) @@ -510,6 +512,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(ERR_get_error, 749, libs.second ) RESOLVEFUNC(EVP_des_ede3_cbc, 919, libs.second ) RESOLVEFUNC(EVP_PKEY_assign, 859, libs.second ) + RESOLVEFUNC(EVP_PKEY_set1_RSA, 880, libs.second ) + RESOLVEFUNC(EVP_PKEY_set1_DSA, 879, libs.second ) RESOLVEFUNC(EVP_PKEY_free, 867, libs.second ) RESOLVEFUNC(EVP_PKEY_get1_DSA, 869, libs.second ) RESOLVEFUNC(EVP_PKEY_get1_RSA, 870, libs.second ) @@ -632,6 +636,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(ERR_get_error) RESOLVEFUNC(EVP_des_ede3_cbc) RESOLVEFUNC(EVP_PKEY_assign) + RESOLVEFUNC(EVP_PKEY_set1_RSA) + RESOLVEFUNC(EVP_PKEY_set1_DSA) RESOLVEFUNC(EVP_PKEY_free) RESOLVEFUNC(EVP_PKEY_get1_DSA) RESOLVEFUNC(EVP_PKEY_get1_RSA) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index c93d547..ef61dbf 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -227,6 +227,8 @@ char *q_ERR_error_string(unsigned long a, char *b); unsigned long q_ERR_get_error(); const EVP_CIPHER *q_EVP_des_ede3_cbc(); int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c); +int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b); +int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b); void q_EVP_PKEY_free(EVP_PKEY *a); RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a); DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a); |