summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorRohan McGovern <rohan.mcgovern@nokia.com>2011-09-14 07:45:44 (GMT)
committerRohan McGovern <rohan.mcgovern@nokia.com>2011-09-14 07:45:44 (GMT)
commit1b30b3a6726adf806ae221357393e562285e8346 (patch)
treec650ff836d3abdf5890726354ee6e2b9bce11304 /src/network
parent1c1a5fe0e2d9e28be5e0e14732d79d34c9bb2a74 (diff)
parent704dd92581783d91ccd234d58896d7078eed14a5 (diff)
downloadQt-1b30b3a6726adf806ae221357393e562285e8346.zip
Qt-1b30b3a6726adf806ae221357393e562285e8346.tar.gz
Qt-1b30b3a6726adf806ae221357393e562285e8346.tar.bz2
Merge remote branch 'origin/4.8' into 4.8-from-4.7
Conflicts: src/gui/text/qtextengine_p.h src/network/ssl/qsslsocket_openssl.cpp
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri20
-rw-r--r--src/network/access/qftp.cpp17
-rw-r--r--src/network/access/qhttpmultipart.cpp548
-rw-r--r--src/network/access/qhttpmultipart.h119
-rw-r--r--src/network/access/qhttpmultipart_p.h182
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp89
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h13
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp305
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h19
-rw-r--r--src/network/access/qhttpnetworkreply.cpp119
-rw-r--r--src/network/access/qhttpnetworkreply_p.h16
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp17
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp582
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h292
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager.cpp297
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h107
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp111
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h19
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp9
-rw-r--r--src/network/access/qnetworkaccessdatabackend.cpp135
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp14
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp6
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp856
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h44
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp449
-rw-r--r--src/network/access/qnetworkaccessmanager.h5
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h14
-rw-r--r--src/network/access/qnetworkcookie.cpp11
-rw-r--r--src/network/access/qnetworkcookiejar.cpp43
-rw-r--r--src/network/access/qnetworkcookiejar_p.h3
-rw-r--r--src/network/access/qnetworkcookiejartlds_p.h6481
-rw-r--r--src/network/access/qnetworkcookiejartlds_p.h.INFO17
-rw-r--r--src/network/access/qnetworkdiskcache.cpp84
-rw-r--r--src/network/access/qnetworkdiskcache_p.h5
-rw-r--r--src/network/access/qnetworkreply.cpp18
-rw-r--r--src/network/access/qnetworkreply.h1
-rw-r--r--src/network/access/qnetworkreply_p.h3
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp148
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h (renamed from src/network/access/qnetworkaccessdatabackend_p.h)46
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp (renamed from src/network/access/qfilenetworkreply.cpp)93
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h (renamed from src/network/access/qfilenetworkreply_p.h)30
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp198
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h18
-rw-r--r--src/network/access/qnetworkrequest.cpp17
-rw-r--r--src/network/access/qnetworkrequest.h7
-rw-r--r--src/network/access/qnetworkrequest_p.h3
-rw-r--r--src/network/bearer/bearer.pri1
-rw-r--r--src/network/bearer/qbearerengine.cpp14
-rw-r--r--src/network/bearer/qbearerengine_p.h5
-rw-r--r--src/network/bearer/qbearerplugin.cpp4
-rw-r--r--src/network/bearer/qbearerplugin_p.h6
-rw-r--r--src/network/bearer/qnetworkconfigmanager.cpp22
-rw-r--r--src/network/bearer/qnetworkconfigmanager.h22
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.cpp104
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.h46
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp30
-rw-r--r--src/network/bearer/qnetworkconfiguration.h11
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h17
-rw-r--r--src/network/bearer/qnetworksession.cpp142
-rw-r--r--src/network/bearer/qnetworksession.h17
-rw-r--r--src/network/bearer/qnetworksession_p.h40
-rw-r--r--src/network/bearer/qsharednetworksession_p.h4
-rw-r--r--src/network/kernel/kernel.pri5
-rw-r--r--src/network/kernel/qauthenticator.cpp6
-rw-r--r--src/network/kernel/qhostaddress.cpp31
-rw-r--r--src/network/kernel/qhostaddress_p.h5
-rw-r--r--src/network/kernel/qhostinfo.cpp102
-rw-r--r--src/network/kernel/qhostinfo_p.h132
-rw-r--r--src/network/kernel/qhostinfo_symbian.cpp591
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp15
-rw-r--r--src/network/kernel/qhostinfo_win.cpp7
-rw-r--r--src/network/kernel/qnetworkinterface_symbian.cpp138
-rw-r--r--src/network/kernel/qnetworkproxy.cpp111
-rw-r--r--src/network/kernel/qnetworkproxy.h16
-rw-r--r--src/network/kernel/qnetworkproxy_symbian.cpp102
-rw-r--r--src/network/network.pro2
-rw-r--r--src/network/socket/qabstractsocket.cpp227
-rw-r--r--src/network/socket/qabstractsocket.h5
-rw-r--r--src/network/socket/qabstractsocket_p.h5
-rw-r--r--src/network/socket/qabstractsocketengine.cpp14
-rw-r--r--src/network/socket/qabstractsocketengine_p.h18
-rw-r--r--src/network/socket/qhttpsocketengine.cpp46
-rw-r--r--src/network/socket/qhttpsocketengine_p.h9
-rw-r--r--src/network/socket/qlocalserver.cpp2
-rw-r--r--src/network/socket/qlocalserver_p.h2
-rw-r--r--src/network/socket/qlocalsocket.cpp2
-rw-r--r--src/network/socket/qlocalsocket_p.h2
-rw-r--r--src/network/socket/qlocalsocket_win.cpp15
-rw-r--r--src/network/socket/qnativesocketengine.cpp60
-rw-r--r--src/network/socket/qnativesocketengine_p.h25
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp383
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp269
-rw-r--r--src/network/socket/qsocks5socketengine.cpp48
-rw-r--r--src/network/socket/qsocks5socketengine_p.h9
-rw-r--r--src/network/socket/qsymbiansocketengine.cpp1782
-rw-r--r--src/network/socket/qsymbiansocketengine_p.h254
-rw-r--r--src/network/socket/qtcpserver.cpp15
-rw-r--r--src/network/socket/qudpsocket.cpp138
-rw-r--r--src/network/socket/qudpsocket.h13
-rw-r--r--src/network/socket/socket.pri31
-rw-r--r--src/network/ssl/qssl.cpp7
-rw-r--r--src/network/ssl/qssl.h4
-rw-r--r--src/network/ssl/qsslcertificate.cpp46
-rw-r--r--src/network/ssl/qsslconfiguration.cpp52
-rw-r--r--src/network/ssl/qsslconfiguration.h2
-rw-r--r--src/network/ssl/qsslconfiguration_p.h2
-rw-r--r--src/network/ssl/qsslerror.cpp4
-rw-r--r--src/network/ssl/qsslerror.h1
-rw-r--r--src/network/ssl/qsslsocket.cpp89
-rw-r--r--src/network/ssl/qsslsocket.h3
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp92
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h4
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp37
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h15
-rw-r--r--src/network/ssl/qsslsocket_p.h10
116 files changed, 8510 insertions, 8614 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index ce79b06..99e861e 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -8,12 +8,11 @@ HEADERS += \
access/qhttpnetworkreply_p.h \
access/qhttpnetworkconnection_p.h \
access/qhttpnetworkconnectionchannel_p.h \
- access/qfilenetworkreply_p.h \
+ access/qnetworkaccessauthenticationmanager_p.h \
access/qnetworkaccessmanager.h \
access/qnetworkaccessmanager_p.h \
access/qnetworkaccesscache_p.h \
access/qnetworkaccessbackend_p.h \
- access/qnetworkaccessdatabackend_p.h \
access/qnetworkaccessdebugpipebackend_p.h \
access/qnetworkaccesshttpbackend_p.h \
access/qnetworkaccessfilebackend_p.h \
@@ -23,16 +22,20 @@ HEADERS += \
access/qnetworkcookie_p.h \
access/qnetworkcookiejar.h \
access/qnetworkcookiejar_p.h \
- access/qnetworkcookiejartlds_p.h \
access/qnetworkrequest.h \
access/qnetworkrequest_p.h \
access/qnetworkreply.h \
access/qnetworkreply_p.h \
access/qnetworkreplyimpl_p.h \
+ access/qnetworkreplydataimpl_p.h \
+ access/qnetworkreplyfileimpl_p.h \
access/qabstractnetworkcache_p.h \
access/qabstractnetworkcache.h \
access/qnetworkdiskcache_p.h \
- access/qnetworkdiskcache.h
+ access/qnetworkdiskcache.h \
+ access/qhttpthreaddelegate_p.h \
+ access/qhttpmultipart.h \
+ access/qhttpmultipart_p.h
SOURCES += \
access/qftp.cpp \
@@ -42,11 +45,10 @@ SOURCES += \
access/qhttpnetworkreply.cpp \
access/qhttpnetworkconnection.cpp \
access/qhttpnetworkconnectionchannel.cpp \
- access/qfilenetworkreply.cpp \
+ access/qnetworkaccessauthenticationmanager.cpp \
access/qnetworkaccessmanager.cpp \
access/qnetworkaccesscache.cpp \
access/qnetworkaccessbackend.cpp \
- access/qnetworkaccessdatabackend.cpp \
access/qnetworkaccessdebugpipebackend.cpp \
access/qnetworkaccessfilebackend.cpp \
access/qnetworkaccesscachebackend.cpp \
@@ -57,7 +59,11 @@ SOURCES += \
access/qnetworkrequest.cpp \
access/qnetworkreply.cpp \
access/qnetworkreplyimpl.cpp \
+ access/qnetworkreplydataimpl.cpp \
+ access/qnetworkreplyfileimpl.cpp \
access/qabstractnetworkcache.cpp \
- access/qnetworkdiskcache.cpp
+ access/qnetworkdiskcache.cpp \
+ access/qhttpthreaddelegate.cpp \
+ access/qhttpmultipart.cpp
include($$PWD/../../3rdparty/zlib_dependency.pri)
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
index b832593..50a3b1e 100644
--- a/src/network/access/qftp.cpp
+++ b/src/network/access/qftp.cpp
@@ -319,6 +319,10 @@ void QFtpDTP::connectToHost(const QString & host, quint16 port)
socket = 0;
}
socket = new QTcpSocket(this);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket
+ socket->setProperty("_q_networksession", property("_q_networksession"));
+#endif
socket->setObjectName(QLatin1String("QFtpDTP Passive state socket"));
connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
@@ -331,6 +335,10 @@ void QFtpDTP::connectToHost(const QString & host, quint16 port)
int QFtpDTP::setupListener(const QHostAddress &address)
{
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket
+ listener.setProperty("_q_networksession", property("_q_networksession"));
+#endif
if (!listener.isListening() && !listener.listen(address, 0))
return -1;
return listener.serverPort();
@@ -808,6 +816,11 @@ QFtpPI::QFtpPI(QObject *parent) :
void QFtpPI::connectToHost(const QString &host, quint16 port)
{
emit connectState(QFtp::HostLookup);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket & DTP
+ commandSocket.setProperty("_q_networksession", property("_q_networksession"));
+ dtp.setProperty("_q_networksession", property("_q_networksession"));
+#endif
commandSocket.connectToHost(host, port);
}
@@ -2240,6 +2253,10 @@ void QFtpPrivate::_q_startNextCommand()
c->rawCmds.clear();
_q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));
} else if (c->command == QFtp::ConnectToHost) {
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the PI
+ pi.setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
if (!proxyHost.isEmpty()) {
host = c->rawCmds[0];
port = c->rawCmds[1].toUInt();
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
new file mode 100644
index 0000000..7545658
--- /dev/null
+++ b/src/network/access/qhttpmultipart.cpp
@@ -0,0 +1,548 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpmultipart.h"
+#include "qhttpmultipart_p.h"
+#include "QtCore/qdatetime.h" // for initializing the random number generator with QTime
+#include "QtCore/qmutex.h"
+#include "QtCore/qthreadstorage.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHttpPart
+ \brief The QHttpPart class holds a body part to be used inside a
+ HTTP multipart MIME message.
+ \since 4.8
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QHttpPart class holds a body part to be used inside a HTTP
+ multipart MIME message (which is represented by the QHttpMultiPart class).
+ A QHttpPart consists of a header block
+ and a data block, which are separated by each other by two
+ consecutive new lines. An example for one part would be:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 0
+
+ For setting headers, use setHeader() and setRawHeader(), which behave
+ exactly like QNetworkRequest::setHeader() and QNetworkRequest::setRawHeader().
+
+ For reading small pieces of data, use setBody(); for larger data blocks
+ like e.g. images, use setBodyDevice(). The latter method saves memory by
+ not copying the data internally, but reading directly from the device.
+ This means that the device must be opened and readable at the moment when
+ the multipart message containing the body part is sent on the network via
+ QNetworkAccessManager::post().
+
+ To construct a QHttpPart with a small body, consider the following snippet
+ (this produces the data shown in the example above):
+
+ \snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 1
+
+ To construct a QHttpPart reading from a device (e.g. a file), the following
+ can be applied:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 2
+
+ Be aware that QHttpPart does not take ownership of the device when set, so
+ it is the developer's responsibility to destroy it when it is not needed anymore.
+ A good idea might be to set the multipart message as parent object for the device,
+ as documented at the documentation for QHttpMultiPart.
+
+ \sa QHttpMultiPart, QNetworkAccessManager
+*/
+
+
+/*!
+ Constructs an empty QHttpPart object.
+*/
+QHttpPart::QHttpPart() : d(new QHttpPartPrivate)
+{
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpPart::QHttpPart(const QHttpPart &other) : d(other.d)
+{
+}
+
+/*!
+ Destroys this QHttpPart.
+*/
+QHttpPart::~QHttpPart()
+{
+ d = 0;
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpPart &QHttpPart::operator=(const QHttpPart &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this object is the same as \a other (i.e., if they
+ have the same headers and body).
+
+ \sa operator!=()
+*/
+bool QHttpPart::operator==(const QHttpPart &other) const
+{
+ return d == other.d || *d == *other.d;
+}
+
+/*!
+ \fn bool QHttpPart::operator!=(const QHttpPart &other) const
+
+ Returns true if this object is not the same as \a other.
+
+ \sa operator==()
+*/
+
+/*!
+ Sets the value of the known header \a header to be \a value,
+ overriding any previously set headers.
+
+ \sa QNetworkRequest::KnownHeaders, setRawHeader(), QNetworkRequest::setHeader()
+*/
+void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+{
+ d->setCookedHeader(header, value);
+}
+
+/*!
+ Sets the header \a headerName to be of value \a headerValue. If \a
+ headerName corresponds to a known header (see
+ QNetworkRequest::KnownHeaders), the raw format will be parsed and
+ the corresponding "cooked" header will be set as well.
+
+ Note: setting the same header twice overrides the previous
+ setting. To accomplish the behaviour of multiple HTTP headers of
+ the same name, you should concatenate the two values, separating
+ them with a comma (",") and set one single raw header.
+
+ \sa QNetworkRequest::KnownHeaders, setHeader(), QNetworkRequest::setRawHeader()
+*/
+void QHttpPart::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
+{
+ d->setRawHeader(headerName, headerValue);
+}
+
+/*!
+ Sets the body of this MIME part to \a body. The body set with this method
+ will be used unless the device is set via setBodyDevice(). For a large
+ amount of data (e.g. an image), use setBodyDevice(), which will not copy
+ the data internally.
+
+ \sa setBodyDevice()
+*/
+void QHttpPart::setBody(const QByteArray &body)
+{
+ d->setBody(body);
+}
+
+/*!
+ Sets the device to read the content from to \a device. For large amounts of data
+ this method should be preferred over setBody(),
+ because the content is not copied when using this method, but read
+ directly from the device.
+ \a device must be open and readable. QHttpPart does not take ownership
+ of \a device, i.e. the device must be closed and destroyed if necessary.
+ if \a device is sequential (e.g. sockets, but not files),
+ QNetworkAccessManager::post() should be called after \a device has
+ emitted finished().
+ For unsetting the device and using data set via setBody(), use
+ "setBodyDevice(0)".
+
+ \sa setBody(), QNetworkAccessManager::post()
+ */
+void QHttpPart::setBodyDevice(QIODevice *device)
+{
+ d->setBodyDevice(device);
+}
+
+
+
+/*!
+ \class QHttpMultiPart
+ \brief The QHttpMultiPart class resembles a MIME multipart message to be sent over HTTP.
+ \since 4.8
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QHttpMultiPart resembles a MIME multipart message, as described in RFC 2046,
+ which is to be sent over HTTP.
+ A multipart message consists of an arbitrary number of body parts (see QHttpPart),
+ which are separated by a unique boundary. The boundary of the QHttpMultiPart is
+ constructed with the string "boundary_.oOo._" followed by random characters,
+ and provides enough uniqueness to make sure it does not occur inside the parts itself.
+ If desired, the boundary can still be set via setBoundary().
+
+ As an example, consider the following code snippet, which constructs a multipart
+ message containing a text part followed by an image part:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttpmultipart.cpp 0
+
+ \sa QHttpPart, QNetworkAccessManager::post()
+*/
+
+/*!
+ \enum QHttpMultiPart::ContentType
+
+ List of known content types for a multipart subtype as described
+ in RFC 2046 and others.
+
+ \value MixedType corresponds to the "multipart/mixed" subtype,
+ meaning the body parts are independent of each other, as described
+ in RFC 2046.
+
+ \value RelatedType corresponds to the "multipart/related" subtype,
+ meaning the body parts are related to each other, as described in RFC 2387.
+
+ \value FormDataType corresponds to the "multipart/form-data"
+ subtype, meaning the body parts contain form elements, as described in RFC 2388.
+
+ \value AlternativeType corresponds to the "multipart/alternative"
+ subtype, meaning the body parts are alternative representations of
+ the same information, as described in RFC 2046.
+
+ \sa setContentType()
+*/
+
+/*!
+ Constructs a QHttpMultiPart with content type MixedType and sets
+ \a parent as the parent object.
+
+ \sa QHttpMultiPart::ContentType
+*/
+QHttpMultiPart::QHttpMultiPart(QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
+{
+ Q_D(QHttpMultiPart);
+ d->contentType = MixedType;
+}
+
+/*!
+ Constructs a QHttpMultiPart with content type \a contentType and
+ sets parent as the parent object.
+
+ \sa QHttpMultiPart::ContentType
+*/
+QHttpMultiPart::QHttpMultiPart(QHttpMultiPart::ContentType contentType, QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
+{
+ Q_D(QHttpMultiPart);
+ d->contentType = contentType;
+}
+
+/*!
+ Destroys the multipart.
+*/
+QHttpMultiPart::~QHttpMultiPart()
+{
+}
+
+/*!
+ Appends \a httpPart to this multipart.
+*/
+void QHttpMultiPart::append(const QHttpPart &httpPart)
+{
+ d_func()->parts.append(httpPart);
+}
+
+/*!
+ Sets the content type to \a contentType. The content type will be used
+ in the HTTP header section when sending the multipart message via
+ QNetworkAccessManager::post().
+ In case you want to use a multipart subtype not contained in
+ QHttpMultiPart::ContentType,
+ you can add the "Content-Type" header field to the QNetworkRequest
+ by hand, and then use this request together with the multipart
+ message for posting.
+
+ \sa QHttpMultiPart::ContentType, QNetworkAccessManager::post()
+*/
+void QHttpMultiPart::setContentType(QHttpMultiPart::ContentType contentType)
+{
+ d_func()->contentType = contentType;
+}
+
+/*!
+ returns the boundary.
+
+ \sa setBoundary()
+*/
+QByteArray QHttpMultiPart::boundary() const
+{
+ return d_func()->boundary;
+}
+
+/*!
+ Sets the boundary to \a boundary.
+
+ Usually, you do not need to generate a boundary yourself; upon construction
+ the boundary is initiated with the string "boundary_.oOo._" followed by random
+ characters, and provides enough uniqueness to make sure it does not occur
+ inside the parts itself.
+
+ \sa boundary()
+*/
+void QHttpMultiPart::setBoundary(const QByteArray &boundary)
+{
+ d_func()->boundary = boundary;
+}
+
+
+
+// ------------------------------------------------------------------
+// ----------- implementations of private classes: ------------------
+// ------------------------------------------------------------------
+
+
+
+qint64 QHttpPartPrivate::bytesAvailable() const
+{
+ checkHeaderCreated();
+ qint64 bytesAvailable = header.count();
+ if (bodyDevice) {
+ bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
+ } else {
+ bytesAvailable += body.count() - readPointer;
+ }
+ // the device might have closed etc., so make sure we do not return a negative value
+ return qMax(bytesAvailable, (qint64) 0);
+}
+
+qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
+{
+ checkHeaderCreated();
+ qint64 bytesRead = 0;
+ qint64 headerDataCount = header.count();
+
+ // read header if it has not been read yet
+ if (readPointer < headerDataCount) {
+ bytesRead = qMin(headerDataCount - readPointer, maxSize);
+ const char *headerData = header.constData();
+ memcpy(data, headerData + readPointer, bytesRead);
+ readPointer += bytesRead;
+ }
+ // read content if there is still space
+ if (bytesRead < maxSize) {
+ if (bodyDevice) {
+ qint64 dataBytesRead = bodyDevice->read(data + bytesRead, maxSize - bytesRead);
+ if (dataBytesRead == -1)
+ return -1;
+ bytesRead += dataBytesRead;
+ readPointer += dataBytesRead;
+ } else {
+ qint64 contentBytesRead = qMin(body.count() - readPointer + headerDataCount, maxSize - bytesRead);
+ const char *contentData = body.constData();
+ // if this method is called several times, we need to find the
+ // right offset in the content ourselves:
+ memcpy(data + bytesRead, contentData + readPointer - headerDataCount, contentBytesRead);
+ bytesRead += contentBytesRead;
+ readPointer += contentBytesRead;
+ }
+ }
+ return bytesRead;
+}
+
+qint64 QHttpPartPrivate::size() const
+{
+ checkHeaderCreated();
+ qint64 size = header.count();
+ if (bodyDevice) {
+ size += bodyDevice->size();
+ } else {
+ size += body.count();
+ }
+ return size;
+}
+
+bool QHttpPartPrivate::reset()
+{
+ bool ret = true;
+ if (bodyDevice)
+ if (!bodyDevice->reset())
+ ret = false;
+ readPointer = 0;
+ return ret;
+}
+void QHttpPartPrivate::checkHeaderCreated() const
+{
+ if (!headerCreated) {
+ // copied from QHttpNetworkRequestPrivate::header() and adapted
+ QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
+ QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
+ for (; it != fields.constEnd(); ++it)
+ header += it->first + ": " + it->second + "\r\n";
+ header += "\r\n";
+ headerCreated = true;
+ }
+}
+
+Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage);
+
+QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
+{
+ if (!seedCreatedStorage()->hasLocalData()) {
+ qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
+ seedCreatedStorage()->setLocalData(new bool(true));
+ }
+
+ boundary = QByteArray("boundary_.oOo._")
+ + QByteArray::number(qrand()).toBase64()
+ + QByteArray::number(qrand()).toBase64()
+ + QByteArray::number(qrand()).toBase64();
+
+ // boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
+ if (boundary.count() > 70)
+ boundary = boundary.left(70);
+}
+
+qint64 QHttpMultiPartIODevice::size() const
+{
+ // if not done yet, we calculate the size and the offsets of each part,
+ // including boundary (needed later in readData)
+ if (deviceSize == -1) {
+ qint64 currentSize = 0;
+ qint64 boundaryCount = multiPart->boundary.count();
+ for (int a = 0; a < multiPart->parts.count(); a++) {
+ partOffsets.append(currentSize);
+ // 4 additional bytes for the "--" before and the "\r\n" after the boundary,
+ // and 2 bytes for the "\r\n" after the content
+ currentSize += boundaryCount + 4 + multiPart->parts.at(a).d->size() + 2;
+ }
+ currentSize += boundaryCount + 4; // size for ending boundary and 2 beginning and ending dashes
+ deviceSize = currentSize;
+ }
+ return deviceSize;
+}
+
+bool QHttpMultiPartIODevice::isSequential() const
+{
+ for (int a = 0; a < multiPart->parts.count(); a++) {
+ QIODevice *device = multiPart->parts.at(a).d->bodyDevice;
+ // we are sequential if any of the bodyDevices of our parts are sequential;
+ // when reading from a byte array, we are not sequential
+ if (device && device->isSequential())
+ return true;
+ }
+ return false;
+}
+
+bool QHttpMultiPartIODevice::reset()
+{
+ for (int a = 0; a < multiPart->parts.count(); a++)
+ if (!multiPart->parts[a].d->reset())
+ return false;
+ return true;
+}
+qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
+{
+ qint64 bytesRead = 0, index = 0;
+
+ // skip the parts we have already read
+ while (index < multiPart->parts.count() &&
+ readPointer >= partOffsets.at(index) + multiPart->parts.at(index).d->size())
+ index++;
+
+ // read the data
+ while (bytesRead < maxSize && index < multiPart->parts.count()) {
+
+ // check whether we need to read the boundary of the current part
+ QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
+ qint64 boundaryCount = boundaryData.count();
+ qint64 partIndex = readPointer - partOffsets.at(index);
+ if (partIndex < boundaryCount) {
+ qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
+ memcpy(data + bytesRead, boundaryData.constData() + partIndex, boundaryBytesRead);
+ bytesRead += boundaryBytesRead;
+ readPointer += boundaryBytesRead;
+ partIndex += boundaryBytesRead;
+ }
+
+ // check whether we need to read the data of the current part
+ if (bytesRead < maxSize && partIndex >= boundaryCount && partIndex < boundaryCount + multiPart->parts.at(index).d->size()) {
+ qint64 dataBytesRead = multiPart->parts[index].d->readData(data + bytesRead, maxSize - bytesRead);
+ if (dataBytesRead == -1)
+ return -1;
+ bytesRead += dataBytesRead;
+ readPointer += dataBytesRead;
+ partIndex += dataBytesRead;
+ }
+
+ // check whether we need to read the ending CRLF of the current part
+ if (bytesRead < maxSize && partIndex >= boundaryCount + multiPart->parts.at(index).d->size()) {
+ if (bytesRead == maxSize - 1)
+ return bytesRead;
+ memcpy(data + bytesRead, "\r\n", 2);
+ bytesRead += 2;
+ readPointer += 2;
+ index++;
+ }
+ }
+ // check whether we need to return the final boundary
+ if (bytesRead < maxSize && index == multiPart->parts.count()) {
+ QByteArray finalBoundary = "--" + multiPart->boundary + "--";
+ qint64 boundaryIndex = readPointer + finalBoundary.count() - size();
+ qint64 lastBoundaryBytesRead = qMin(finalBoundary.count() - boundaryIndex, maxSize - bytesRead);
+ memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
+ bytesRead += lastBoundaryBytesRead;
+ readPointer += lastBoundaryBytesRead;
+ }
+ return bytesRead;
+}
+
+qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ return -1;
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
new file mode 100644
index 0000000..76e8e14
--- /dev/null
+++ b/src/network/access/qhttpmultipart.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPMULTIPART_H
+#define QHTTPMULTIPART_H
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QByteArray>
+#include <QtNetwork/QNetworkRequest>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QHttpPartPrivate;
+class QHttpMultiPart;
+
+class Q_NETWORK_EXPORT QHttpPart
+{
+public:
+ QHttpPart();
+ QHttpPart(const QHttpPart &other);
+ ~QHttpPart();
+ QHttpPart &operator=(const QHttpPart &other);
+ bool operator==(const QHttpPart &other) const;
+ inline bool operator!=(const QHttpPart &other) const
+ { return !operator==(other); }
+
+ void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue);
+
+ void setBody(const QByteArray &body);
+ void setBodyDevice(QIODevice *device);
+
+private:
+ QSharedDataPointer<QHttpPartPrivate> d;
+
+ friend class QHttpMultiPartIODevice;
+};
+
+class QHttpMultiPartPrivate;
+
+class Q_NETWORK_EXPORT QHttpMultiPart : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ enum ContentType {
+ MixedType,
+ RelatedType,
+ FormDataType,
+ AlternativeType
+ };
+
+ QHttpMultiPart(QObject *parent = 0);
+ QHttpMultiPart(ContentType contentType, QObject *parent = 0);
+ ~QHttpMultiPart();
+
+ void append(const QHttpPart &httpPart);
+
+ void setContentType(ContentType contentType);
+
+ QByteArray boundary() const;
+ void setBoundary(const QByteArray &boundary);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpMultiPart)
+ Q_DISABLE_COPY(QHttpMultiPart)
+
+ friend class QNetworkAccessManager;
+ friend class QNetworkAccessManagerPrivate;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QHTTPMULTIPART_H
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
new file mode 100644
index 0000000..f57b0eb
--- /dev/null
+++ b/src/network/access/qhttpmultipart_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPMULTIPART_P_H
+#define QHTTPMULTIPART_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qshareddata.h"
+#include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+class QHttpPartPrivate: public QSharedData, public QNetworkHeadersPrivate
+{
+public:
+ inline QHttpPartPrivate() : bodyDevice(0), headerCreated(false), readPointer(0)
+ {
+ }
+ ~QHttpPartPrivate()
+ {
+ }
+
+
+ QHttpPartPrivate(const QHttpPartPrivate &other)
+ : QSharedData(other), QNetworkHeadersPrivate(other), body(other.body),
+ header(other.header), headerCreated(other.headerCreated), readPointer(other.readPointer)
+ {
+ bodyDevice = other.bodyDevice;
+ }
+
+ inline bool operator==(const QHttpPartPrivate &other) const
+ {
+ return rawHeaders == other.rawHeaders && body == other.body &&
+ bodyDevice == other.bodyDevice && readPointer == other.readPointer;
+ }
+
+ void setBodyDevice(QIODevice *device) {
+ bodyDevice = device;
+ readPointer = 0;
+ }
+ void setBody(const QByteArray &newBody) {
+ body = newBody;
+ readPointer = 0;
+ }
+
+ // QIODevice-style methods called by QHttpMultiPartIODevice (but this class is
+ // not a QIODevice):
+ qint64 bytesAvailable() const;
+ qint64 readData(char *data, qint64 maxSize);
+ qint64 size() const;
+ bool reset();
+
+ QByteArray body;
+ QIODevice *bodyDevice;
+
+private:
+ void checkHeaderCreated() const;
+
+ mutable QByteArray header;
+ mutable bool headerCreated;
+ qint64 readPointer;
+};
+
+
+
+class QHttpMultiPartPrivate;
+
+class Q_AUTOTEST_EXPORT QHttpMultiPartIODevice : public QIODevice
+{
+public:
+ QHttpMultiPartIODevice(QHttpMultiPartPrivate *parentMultiPart) :
+ QIODevice(), multiPart(parentMultiPart), readPointer(0), deviceSize(-1) {
+ }
+
+ ~QHttpMultiPartIODevice() {
+ }
+
+ virtual bool atEnd() const {
+ return readPointer == size();
+ }
+
+ virtual qint64 bytesAvailable() const {
+ return size() - readPointer;
+ }
+
+ virtual void close() {
+ readPointer = 0;
+ partOffsets.clear();
+ deviceSize = -1;
+ QIODevice::close();
+ }
+
+ virtual qint64 bytesToWrite() const {
+ return 0;
+ }
+
+ virtual qint64 size() const;
+ virtual bool isSequential() const;
+ virtual bool reset();
+ virtual qint64 readData(char *data, qint64 maxSize);
+ virtual qint64 writeData(const char *data, qint64 maxSize);
+
+ QHttpMultiPartPrivate *multiPart;
+ qint64 readPointer;
+ mutable QList<qint64> partOffsets;
+ mutable qint64 deviceSize;
+};
+
+
+
+class QHttpMultiPartPrivate: public QObjectPrivate
+{
+public:
+
+ QHttpMultiPartPrivate();
+
+ ~QHttpMultiPartPrivate()
+ {
+ delete device;
+ }
+
+ QList<QHttpPart> parts;
+ QByteArray boundary;
+ QHttpMultiPart::ContentType contentType;
+ QHttpMultiPartIODevice *device;
+
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QHTTPMULTIPART_P_H
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index d76a5fd..6101eea 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -119,6 +119,11 @@ void QHttpNetworkConnectionPrivate::init()
{
for (int i = 0; i < channelCount; i++) {
channels[i].setConnection(this->q_func());
+ channels[i].ssl = encrypt;
+#ifndef QT_NO_BEARERMANAGEMENT
+ //push session down to channels
+ channels[i].networkSession = networkSession;
+#endif
channels[i].init();
}
}
@@ -295,7 +300,13 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
int i = indexOf(socket);
// remove the corrupt data if any
reply->d_func()->eraseData();
+
+ // Clean the channel
channels[i].close();
+ channels[i].reply = 0;
+ channels[i].request = QHttpNetworkRequest();
+ channels[i].requeueCurrentlyPipelinedRequests();
+
// send the next request
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -490,7 +501,7 @@ void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
-void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
{
Q_ASSERT(socket);
@@ -503,8 +514,7 @@ void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socke
prepareRequest(messagePair);
channels[i].request = messagePair.first;
channels[i].reply = messagePair.second;
- channels[i].sendRequest();
- return;
+ return true;
}
if (!lowPriorityQueue.isEmpty()) {
@@ -514,9 +524,18 @@ void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socke
prepareRequest(messagePair);
channels[i].request = messagePair.first;
channels[i].reply = messagePair.second;
- channels[i].sendRequest();
- return;
+ return true;
}
+ return false;
+}
+
+QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest()
+{
+ if (!highPriorityQueue.isEmpty())
+ return highPriorityQueue.last().first;
+ if (!lowPriorityQueue.isEmpty())
+ return lowPriorityQueue.last().first;
+ return QHttpNetworkRequest();
}
// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
@@ -567,33 +586,35 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
|| channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
return;
-
- //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing highPriorityQueue, size=" << highPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
int lengthBefore;
while (!highPriorityQueue.isEmpty()) {
lengthBefore = channels[i].alreadyPipelinedRequests.length();
fillPipeline(highPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
+ if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ channels[i].pipelineFlush();
return;
+ }
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
break; // did not process anything, now do the low prio queue
}
- //qDebug() << "QHttpNetworkConnectionPrivate::fillPipeline processing lowPriorityQueue, size=" << lowPriorityQueue.size() << " alreadyPipelined=" << channels[i].alreadyPipelinedRequests.length();
while (!lowPriorityQueue.isEmpty()) {
lengthBefore = channels[i].alreadyPipelinedRequests.length();
fillPipeline(lowPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
+ if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ channels[i].pipelineFlush();
return;
+ }
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
break; // did not process anything
}
+ channels[i].pipelineFlush();
}
// returns true when the processing of a queue has been done
@@ -766,7 +787,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
//resend the necessary ones.
for (int i = 0; i < channelCount; ++i) {
- if (channels[i].resendCurrent) {
+ if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
channels[i].resendCurrent = false;
channels[i].state = QHttpNetworkConnectionChannel::IdleState;
@@ -785,17 +806,8 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// try to get a free AND connected socket
for (int i = 0; i < channelCount; ++i) {
if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
- dequeueAndSendRequest(channels[i].socket);
- }
- }
-
- // return fast if there is nothing to do
- if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
- return;
- // try to get a free unconnected socket
- for (int i = 0; i < channelCount; ++i) {
- if (!channels[i].reply && !channels[i].isSocketBusy()) {
- dequeueAndSendRequest(channels[i].socket);
+ if (dequeueRequest(channels[i].socket))
+ channels[i].sendRequest();
}
}
@@ -812,6 +824,21 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
for (int i = 0; i < channelCount; i++)
if (channels[i].socket->state() == QAbstractSocket::ConnectedState)
fillPipeline(channels[i].socket);
+
+ // If there is not already any connected channels we need to connect a new one.
+ // We do not pair the channel with the request until we know if it is
+ // connected or not. This is to reuse connected channels before we connect new once.
+ int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count();
+ for (int i = 0; i < channelCount; ++i) {
+ if (channels[i].socket->state() == QAbstractSocket::ConnectingState)
+ queuedRequest--;
+ if ( queuedRequest <=0 )
+ break;
+ if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
+ channels[i].ensureConnection();
+ queuedRequest--;
+ }
+ }
}
@@ -826,6 +853,23 @@ void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
}
}
+#ifndef QT_NO_BEARERMANAGEMENT
+QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+ : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
+{
+ Q_D(QHttpNetworkConnection);
+ d->networkSession = networkSession;
+ d->init();
+}
+
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+{
+ Q_D(QHttpNetworkConnection);
+ d->networkSession = networkSession;
+ d->init();
+}
+#else
QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
: QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
{
@@ -839,6 +883,7 @@ QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QS
Q_D(QHttpNetworkConnection);
d->init();
}
+#endif
QHttpNetworkConnection::~QHttpNetworkConnection()
{
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 420fac1..6c953be 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -55,6 +55,7 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qnetworksession.h>
#include <private/qobject_p.h>
#include <qauthenticator.h>
@@ -88,8 +89,13 @@ class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
Q_OBJECT
public:
+#ifndef QT_NO_BEARERMANAGEMENT
+ QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+ QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0, QSharedPointer<QNetworkSession> networkSession = QSharedPointer<QNetworkSession>());
+#else
QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
+#endif
~QHttpNetworkConnection();
//The hostname to which this is connected to.
@@ -161,8 +167,9 @@ public:
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
- void dequeueAndSendRequest(QAbstractSocket *socket);
+ bool dequeueRequest(QAbstractSocket *socket);
void prepareRequest(HttpMessagePair &request);
+ QHttpNetworkRequest predictNextRequest();
void fillPipeline(QAbstractSocket *socket);
bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
@@ -208,6 +215,10 @@ public:
QList<HttpMessagePair> highPriorityQueue;
QList<HttpMessagePair> lowPriorityQueue;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
+
friend class QHttpNetworkConnectionChannel;
};
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 7c2e2a1..9b2a6e8 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -54,12 +54,17 @@
# include <QtNetwork/qsslconfiguration.h>
#endif
+#ifndef QT_NO_BEARERMANAGEMENT
+#include "private/qnetworksession_p.h"
+#endif
+
QT_BEGIN_NAMESPACE
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
: socket(0)
+ , ssl(false)
, state(IdleState)
, reply(0)
, written(0)
@@ -90,11 +95,15 @@ void QHttpNetworkConnectionChannel::init()
#else
socket = new QTcpSocket;
#endif
-
- // limit the socket read buffer size. we will read everything into
- // the QHttpNetworkReply anyway, so let's grow only that and not
- // here and there.
- socket->setReadBufferSize(64*1024);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //push session down to socket
+ if (networkSession)
+ socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+#ifndef QT_NO_NETWORKPROXY
+ // Set by QNAM anyway, but let's be safe here
+ socket->setProxy(QNetworkProxy::NoProxy);
+#endif
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
this, SLOT(_q_bytesWritten(qint64)),
@@ -147,10 +156,12 @@ void QHttpNetworkConnectionChannel::init()
void QHttpNetworkConnectionChannel::close()
{
- socket->blockSignals(true);
+ if (socket->state() == QAbstractSocket::UnconnectedState)
+ state = QHttpNetworkConnectionChannel::IdleState;
+ else
+ state = QHttpNetworkConnectionChannel::ClosingState;
+
socket->close();
- socket->blockSignals(false);
- state = QHttpNetworkConnectionChannel::IdleState;
}
@@ -174,11 +185,12 @@ bool QHttpNetworkConnectionChannel::sendRequest()
written = 0; // excluding the header
bytesTotal = 0;
- reply->d_func()->clear();
- reply->d_func()->connection = connection;
- reply->d_func()->connectionChannel = this;
- reply->d_func()->autoDecompress = request.d->autoDecompress;
- reply->d_func()->pipeliningUsed = false;
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ replyPrivate->clear();
+ replyPrivate->connection = connection;
+ replyPrivate->connectionChannel = this;
+ replyPrivate->autoDecompress = request.d->autoDecompress;
+ replyPrivate->pipeliningUsed = false;
// if the url contains authentication parameters, use the new ones
// both channels will use the new authentication parameters
@@ -254,7 +266,7 @@ bool QHttpNetworkConnectionChannel::sendRequest()
#endif
{
// get pointer to upload data
- qint64 currentReadSize;
+ qint64 currentReadSize = 0;
qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
@@ -337,7 +349,6 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
- qint64 bytes = 0;
QAbstractSocket::SocketState socketState = socket->state();
// connection might be closed to signal the end of data
@@ -358,12 +369,14 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
}
// read loop for the response
- while (socket->bytesAvailable()) {
+ qint64 bytes = 0;
+ qint64 lastBytes = bytes;
+ do {
+ lastBytes = bytes;
+
QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
switch (state) {
case QHttpNetworkReplyPrivate::NothingDoneState: {
- // only eat whitespace on the first call
- eatWhitespace();
state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
// fallthrough
}
@@ -387,6 +400,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
return;
}
bytes += headerBytes;
+ // If headers were parsed successfully now it is the ReadingDataState
if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) {
// remove the Content-Length from header
@@ -401,6 +415,10 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
}
if (replyPrivate->shouldEmitSignals())
emit reply->headerChanged();
+ // After headerChanged had been emitted
+ // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
+ // this is handled in the ReadingDataState however
+
if (!replyPrivate->expectContent()) {
replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
allDone();
@@ -422,22 +440,29 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
// to the read buffer maximum size, but we don't care since they should be small.
return;
}
- if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
- // bulk files like images should fulfill these properties and
- // we can therefore save on memory copying
- bytes = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
- replyPrivate->totalProgress += bytes;
+
+ if (replyPrivate->userProvidedDownloadBuffer) {
+ // the user provided a direct buffer where we should put all our data in.
+ // this only works when we can tell the user the content length and he/she can allocate
+ // the buffer in that size.
+ // note that this call will read only from the still buffered data
+ qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+
+ // the user will get notified of it via progress signal
+ if (haveRead > 0)
+ emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
+ && replyPrivate->bodyLength > 0) {
+ // bulk files like images should fulfill these properties and
+ // we can therefore save on memory copying
+ qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
if (replyPrivate->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = reply;
emit reply->readyRead();
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
}
}
else
@@ -445,8 +470,9 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
// use the traditional slower reading (for compressed encoding, chunked encoding,
// no content-length etc)
QByteDataBuffer byteDatas;
- bytes = replyPrivate->readBody(socket, &byteDatas);
- if (bytes) {
+ qint64 haveRead = replyPrivate->readBody(socket, &byteDatas);
+ if (haveRead) {
+ bytes += haveRead;
if (replyPrivate->autoDecompress)
replyPrivate->appendCompressedReplyData(byteDatas);
else
@@ -455,22 +481,16 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
if (!replyPrivate->autoDecompress) {
replyPrivate->totalProgress += bytes;
if (replyPrivate->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = reply;
// important: At the point of this readyRead(), the byteDatas list must be empty,
// else implicit sharing will trigger memcpy when the user is reading data!
emit reply->readyRead();
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return;
}
}
#ifndef QT_NO_COMPRESS
else if (!expand(false)) { // expand a chunk if possible
- return; // ### expand failed
+ // If expand() failed we can just return, it had already called connection->emitReplyError()
+ return;
}
#endif
}
@@ -487,12 +507,13 @@ void QHttpNetworkConnectionChannel::_q_receiveReply()
default:
break;
}
- }
+ } while (bytes != lastBytes && reply);
}
// called when unexpectedly reading a -1 or when data is expected but socket is closed
void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
{
+ Q_ASSERT(reply);
if (reconnectAttempts <= 0) {
// too many errors reading/receiving/parsing the status, close the socket and emit error
requeueCurrentlyPipelinedRequests();
@@ -515,7 +536,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// resend this request after we receive the disconnected signal
if (socketState == QAbstractSocket::ClosingState) {
- resendCurrent = true;
+ if (reply)
+ resendCurrent = true;
return false;
}
@@ -530,7 +552,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (socketState != QAbstractSocket::ConnectedState) {
// connect to the host if not already connected.
state = QHttpNetworkConnectionChannel::ConnectingState;
- pendingEncrypt = connection->d_func()->encrypt;
+ pendingEncrypt = ssl;
// reset state
pipeliningSupported = PipeliningSupportUnknown;
@@ -553,29 +575,57 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
#ifndef QT_NO_NETWORKPROXY
// HTTPS always use transparent proxy.
- if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !connection->d_func()->encrypt) {
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
connectHost = connection->d_func()->networkProxy.hostName();
connectPort = connection->d_func()->networkProxy.port();
}
if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
// Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
- QByteArray value = request.headerField("user-agent");
+ QByteArray value;
+ // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
+ if (request.url().isEmpty())
+ value = connection->d_func()->predictNextRequest().headerField("user-agent");
+ else
+ value = request.headerField("user-agent");
if (!value.isEmpty())
socket->setProperty("_q_user-agent", value);
}
#endif
- if (connection->d_func()->encrypt) {
+ if (ssl) {
#ifndef QT_NO_OPENSSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
sslSocket->connectToHostEncrypted(connectHost, connectPort);
if (ignoreAllSslErrors)
sslSocket->ignoreSslErrors();
sslSocket->ignoreSslErrors(ignoreSslErrorsList);
+
+ // limit the socket read buffer size. we will read everything into
+ // the QHttpNetworkReply anyway, so let's grow only that and not
+ // here and there.
+ socket->setReadBufferSize(64*1024);
#else
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
#endif
} else {
- socket->connectToHost(connectHost, connectPort);
+ // In case of no proxy we can use the Unbuffered QTcpSocket
+#ifndef QT_NO_NETWORKPROXY
+ if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
+ && connection->cacheProxy().type() == QNetworkProxy::NoProxy
+ && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
+#endif
+ socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered);
+ // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
+ socket->setReadBufferSize(1*1024);
+#ifndef QT_NO_NETWORKPROXY
+ } else {
+ socket->connectToHost(connectHost, connectPort);
+
+ // limit the socket read buffer size. we will read everything into
+ // the QHttpNetworkReply anyway, so let's grow only that and not
+ // here and there.
+ socket->setReadBufferSize(64*1024);
+ }
+#endif
}
return false;
}
@@ -608,18 +658,10 @@ bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
reply->d_func()->totalProgress += inflated.size();
reply->d_func()->appendUncompressedReplyData(inflated);
if (reply->d_func()->shouldEmitSignals()) {
- QPointer<QHttpNetworkReply> replyPointer = reply;
// important: At the point of this readyRead(), inflated must be cleared,
// else implicit sharing will trigger memcpy when the user is reading data!
emit reply->readyRead();
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return true;
emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
- // make sure that the reply is valid
- if (replyPointer.isNull())
- return true;
-
}
}
} else {
@@ -634,32 +676,44 @@ bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
void QHttpNetworkConnectionChannel::allDone()
{
+ Q_ASSERT(reply);
#ifndef QT_NO_COMPRESS
// expand the whole data.
- if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd)
- expand(true); // ### if expand returns false, its an error
+ if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) {
+ bool expandResult = expand(true);
+ // If expand() failed we can just return, it had already called connection->emitReplyError()
+ if (!expandResult)
+ return;
+ }
#endif
+
+ if (!reply) {
+ qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.nokia.com/";
+ return;
+ }
+
// while handling 401 & 407, we might reset the status code, so save this.
bool emitFinished = reply->d_func()->shouldEmitSignals();
- handleStatus();
- // ### at this point there should be no more data on the socket
- // close if server requested
bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
- if (connectionCloseEnabled)
- close();
+ detectPipeliningSupport();
+
+ handleStatus();
+ // handleStatus() might have removed the reply because it already called connection->emitReplyError()
+
// queue the finished signal, this is required since we might send new requests from
// slot connected to it. The socket will not fire readyRead signal, if we are already
// in the slot connected to readyRead
- if (emitFinished)
+ if (reply && emitFinished)
QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
+
+
// reset the reconnection attempts after we receive a complete reply.
// in case of failures, each channel will attempt two reconnects before emitting error.
reconnectAttempts = 2;
- detectPipeliningSupport();
-
// now the channel can be seen as free/idle again, all signal emissions for the reply have been done
- this->state = QHttpNetworkConnectionChannel::IdleState;
+ if (state != QHttpNetworkConnectionChannel::ClosingState)
+ state = QHttpNetworkConnectionChannel::IdleState;
// if it does not need to be sent again we can set it to 0
// the previous code did not do that and we had problems with accidental re-sending of a
@@ -697,12 +751,20 @@ void QHttpNetworkConnectionChannel::allDone()
// this was wrong, allDone gets called from that function anyway.
}
} else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
- eatWhitespace();
// this is weird. we had nothing pipelined but still bytes available. better close it.
- if (socket->bytesAvailable() > 0)
- close();
+ //if (socket->bytesAvailable() > 0)
+ // close();
+ //
+ // FIXME
+ // We do not close it anymore now, but should introduce this again after having fixed
+ // the chunked decoder in QHttpNetworkReply to read the whitespace after the last chunk.
+ // (Currently this is worked around by readStatus in the QHttpNetworkReply ignoring
+ // leading whitespace.
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else if (alreadyPipelinedRequests.isEmpty()) {
+ if (connectionCloseEnabled)
+ if (socket->state() != QAbstractSocket::UnconnectedState)
+ close();
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -710,6 +772,7 @@ void QHttpNetworkConnectionChannel::allDone()
void QHttpNetworkConnectionChannel::detectPipeliningSupport()
{
+ Q_ASSERT(reply);
// detect HTTP Pipelining support
QByteArray serverHeaderField;
if (
@@ -747,30 +810,6 @@ void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
-void QHttpNetworkConnectionChannel::eatWhitespace()
-{
- char c;
- do {
- qint64 ret = socket->peek(&c, 1);
-
- // nothing read, fine.
- if (ret == 0)
- return;
-
- // EOF from socket?
- if (ret == -1)
- return; // FIXME, we need to stop processing. however the next stuff done will also do that.
-
- // read all whitespace and line endings
- if (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31) {
- socket->read(&c, 1);
- continue;
- } else {
- break;
- }
- } while(true);
-}
-
void QHttpNetworkConnectionChannel::handleStatus()
{
Q_ASSERT(socket);
@@ -817,6 +856,10 @@ void QHttpNetworkConnectionChannel::handleStatus()
bool QHttpNetworkConnectionChannel::resetUploadData()
{
+ if (!reply) {
+ //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
+ return false;
+ }
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
if (!uploadByteDevice)
return true;
@@ -831,7 +874,7 @@ bool QHttpNetworkConnectionChannel::resetUploadData()
}
-void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
+void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
{
// this is only called for simple GET
@@ -844,21 +887,38 @@ void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
reply->d_func()->pipeliningUsed = true;
#ifndef QT_NO_NETWORKPROXY
- QByteArray header = QHttpNetworkRequestPrivate::header(request,
- (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
+ pipeline.append(QHttpNetworkRequestPrivate::header(request,
+ (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
#else
- QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
+ pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
#endif
- socket->write(header);
alreadyPipelinedRequests.append(pair);
+
+ // pipelineFlush() needs to be called at some point afterwards
}
+void QHttpNetworkConnectionChannel::pipelineFlush()
+{
+ if (pipeline.isEmpty())
+ return;
+
+ // The goal of this is so that we have everything in one TCP packet.
+ // For the Unbuffered QTcpSocket this is manually needed, the buffered
+ // QTcpSocket does it automatically.
+ // Also, sometimes the OS does it for us (Nagle's algorithm) but that
+ // happens only sometimes.
+ socket->write(pipeline);
+ pipeline.clear();
+}
+
+
void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
{
requeueCurrentlyPipelinedRequests();
close();
- resendCurrent = true;
+ if (reply)
+ resendCurrent = true;
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
@@ -886,6 +946,22 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
//private slots
void QHttpNetworkConnectionChannel::_q_readyRead()
{
+ if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
+ // We got a readyRead but no bytes are available..
+ // This happens for the Unbuffered QTcpSocket
+ // Also check if socket is in ConnectedState since
+ // this function may also be invoked via the event loop.
+ char c;
+ qint64 ret = socket->peek(&c, 1);
+ if (ret < 0) {
+ _q_error(socket->error());
+ // We still need to handle the reply so it emits its signals etc.
+ if (reply)
+ _q_receiveReply();
+ return;
+ }
+ }
+
if (isSocketWaiting() || isSocketReading()) {
state = QHttpNetworkConnectionChannel::ReadingState;
if (reply)
@@ -904,6 +980,12 @@ void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
void QHttpNetworkConnectionChannel::_q_disconnected()
{
+ if (state == QHttpNetworkConnectionChannel::ClosingState) {
+ state = QHttpNetworkConnectionChannel::IdleState;
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+
// read the available data before closing
if (isSocketWaiting() || isSocketReading()) {
if (reply) {
@@ -941,10 +1023,10 @@ void QHttpNetworkConnectionChannel::_q_connected()
//channels[i].reconnectAttempts = 2;
if (!pendingEncrypt) {
state = QHttpNetworkConnectionChannel::IdleState;
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
if (reply)
sendRequest();
- else
- close();
}
}
@@ -1010,6 +1092,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
QPointer<QHttpNetworkConnection> that = connection;
QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
+ // Need to dequeu the request so that we can emit the error.
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
if (reply) {
reply->d_func()->errorString = errorString;
emit reply->finishedWithError(errorCode, errorString);
@@ -1025,7 +1110,11 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
- connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
+ // Need to dequeue the request before we can emit the error.
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
}
#endif
@@ -1041,7 +1130,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
return; // ### error
state = QHttpNetworkConnectionChannel::IdleState;
pendingEncrypt = false;
- sendRequest();
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ sendRequest();
}
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
@@ -1052,7 +1144,10 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
// Also pause the connection because socket notifiers may fire while an user
// dialog is displaying
connection->d_func()->pauseConnection();
- emit reply->sslErrors(errors);
+ if (pendingEncrypt && !reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ emit reply->sslErrors(errors);
connection->d_func()->resumeConnection();
}
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index ffb2711..f635cc9 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -95,9 +95,11 @@ public:
WritingState = 2, // writing the data
WaitingState = 4, // waiting for reply
ReadingState = 8, // reading the reply
- BusyState = (ConnectingState|WritingState|WaitingState|ReadingState)
+ ClosingState = 16,
+ BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|ClosingState)
};
QAbstractSocket *socket;
+ bool ssl;
ChannelState state;
QHttpNetworkRequest request; // current request
QHttpNetworkReply *reply; // current reply for this request
@@ -115,6 +117,9 @@ public:
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
#endif
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
// HTTP pipelining -> http://en.wikipedia.org/wiki/Http_pipelining
enum PipeliningSupport {
@@ -124,7 +129,11 @@ public:
};
PipeliningSupport pipeliningSupported;
QList<HttpMessagePair> alreadyPipelinedRequests;
-
+ QByteArray pipeline; // temporary buffer that gets sent to socket in pipelineFlush
+ void pipelineInto(HttpMessagePair &pair);
+ void pipelineFlush();
+ void requeueCurrentlyPipelinedRequests();
+ void detectPipeliningSupport();
QHttpNetworkConnectionChannel();
@@ -144,15 +153,9 @@ public:
bool resetUploadData(); // return true if resetting worked or there is no upload data
- void pipelineInto(HttpMessagePair &pair);
- void requeueCurrentlyPipelinedRequests();
- void detectPipeliningSupport();
-
void handleUnexpectedEOF();
void closeAndResendCurrentRequest();
- void eatWhitespace();
-
bool isSocketBusy() const;
bool isSocketWriting() const;
bool isSocketWaiting() const;
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 0f2fcba..1a02200 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -119,6 +119,7 @@ void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
{
Q_D(QHttpNetworkReply);
d->request = request;
+ d->ssl = request.isSsl();
}
int QHttpNetworkReply::statusCode() const
@@ -176,6 +177,12 @@ qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
return -1;
}
+bool QHttpNetworkReply::readAnyAvailable() const
+{
+ Q_D(const QHttpNetworkReply);
+ return (d->responseData.bufferCount() > 0);
+}
+
QByteArray QHttpNetworkReply::readAny()
{
Q_D(QHttpNetworkReply);
@@ -201,6 +208,25 @@ void QHttpNetworkReply::setDownstreamLimited(bool dsl)
d->connection->d_func()->readMoreLater(this);
}
+bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
+{
+ Q_D(QHttpNetworkReply);
+ return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0);
+}
+
+void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
+{
+ Q_D(QHttpNetworkReply);
+ if (supportsUserProvidedDownloadBuffer())
+ d->userProvidedDownloadBuffer = b;
+}
+
+char* QHttpNetworkReply::userProvidedDownloadBuffer()
+{
+ Q_D(QHttpNetworkReply);
+ return d->userProvidedDownloadBuffer;
+}
+
bool QHttpNetworkReply::isFinished() const
{
return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
@@ -218,7 +244,10 @@ QHttpNetworkConnection* QHttpNetworkReply::connection()
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
+ : QHttpNetworkHeaderPrivate(newUrl)
+ , state(NothingDoneState)
+ , ssl(false)
+ , statusCode(100),
majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
chunkedTransferEncoding(false),
connectionCloseEnabled(true),
@@ -226,6 +255,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
autoDecompress(false), responseData(), requestIsPrepared(false)
,pipeliningUsed(false), downstreamLimited(false)
+ ,userProvidedDownloadBuffer(0)
{
}
@@ -465,6 +495,8 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
return -1; // unexpected EOF
else if (haveRead == 0)
break; // read more later
+ else if (haveRead == 1 && bytes == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
+ continue; // Ignore all whitespace that was trailing froma previous request on that socket
bytes++;
@@ -640,12 +672,32 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
+qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
+{
+ // This first read is to flush the buffer inside the socket
+ qint64 haveRead = 0;
+ haveRead = socket->read(b, bodyLength - contentRead);
+ if (haveRead == -1) {
+ return 0; // ### error checking here;
+ }
+ contentRead += haveRead;
+
+ if (contentRead == bodyLength) {
+ state = AllDoneState;
+ }
+
+ return haveRead;
+}
+
+// note this function can only be used for non-chunked, non-compressed with
+// known content length
qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
{
+
qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
QByteArray bd;
bd.resize(toBeRead);
- qint64 haveRead = socket->read(bd.data(), bd.size());
+ qint64 haveRead = socket->read(bd.data(), toBeRead);
if (haveRead == -1) {
bd.clear();
return 0; // ### error checking here;
@@ -667,29 +719,34 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
{
qint64 bytes = 0;
if (isChunked()) {
- bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
- } else if (bodyLength > 0) { // we have a Content-Length
+ // chunked transfer encoding (rfc 2616, sec 3.6)
+ bytes += readReplyBodyChunked(socket, out);
+ } else if (bodyLength > 0) {
+ // we have a Content-Length
bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
if (contentRead + bytes == bodyLength)
state = AllDoneState;
} else {
+ // no content length. just read what's possible
bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
}
contentRead += bytes;
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
{
+ // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
qint64 bytes = 0;
- Q_ASSERT(in);
+ Q_ASSERT(socket);
Q_ASSERT(out);
- int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
+
while (toBeRead > 0) {
QByteArray byteData;
byteData.resize(toBeRead);
- qint64 haveRead = in->read(byteData.data(), byteData.size());
+ qint64 haveRead = socket->read(byteData.data(), byteData.size());
if (haveRead <= 0) {
// ### error checking here
byteData.clear();
@@ -701,25 +758,35 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer
bytes += haveRead;
size -= haveRead;
- toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
+ toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
}
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
- while (in->bytesAvailable()) { // while we can read from input
- // if we are done with the current chunk, get the size of the new chunk
+ while (socket->bytesAvailable()) {
if (currentChunkRead >= currentChunkSize) {
+ // For the first chunk and when we're done with a chunk
currentChunkSize = 0;
currentChunkRead = 0;
if (bytes) {
+ // After a chunk
char crlf[2];
- bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
+ // read the "\r\n" after the chunk
+ qint64 haveRead = socket->read(crlf, 2);
+ // FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
+ // For nice reasons (the toLong in getChunkSize accepting \n at the beginning
+ // it right now still works, but we should definitely fix this.
+
+ if (haveRead != 2)
+ return bytes; // FIXME
+ bytes += haveRead;
}
- bytes += getChunkSize(in, &currentChunkSize);
+ // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
+ bytes += getChunkSize(socket, &currentChunkSize);
if (currentChunkSize == -1)
break;
}
@@ -729,8 +796,8 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu
break;
}
- // otherwise, try to read what is missing for this chunk
- qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
+ // otherwise, try to begin reading this chunk / to read what is missing for this chunk
+ qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
currentChunkRead += haveRead;
bytes += haveRead;
@@ -740,22 +807,25 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBu
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
{
qint64 bytes = 0;
char crlf[2];
*chunkSize = -1;
- int bytesAvailable = in->bytesAvailable();
+
+ int bytesAvailable = socket->bytesAvailable();
+ // FIXME rewrite to permanent loop without bytesAvailable
while (bytesAvailable > bytes) {
- qint64 sniffedBytes = in->peek(crlf, 2);
+ qint64 sniffedBytes = socket->peek(crlf, 2);
int fragmentSize = fragment.size();
+
// check the next two bytes for a "\r\n", skip blank lines
if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
{
- bytes += in->read(crlf, 1); // read the \r or \n
+ bytes += socket->read(crlf, 1); // read the \r or \n
if (crlf[0] == '\r')
- bytes += in->read(crlf, 1); // read the \n
+ bytes += socket->read(crlf, 1); // read the \n
bool ok = false;
// ignore the chunk-extension
fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
@@ -765,10 +835,15 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
} else {
// read the fragment to the buffer
char c = 0;
- bytes += in->read(&c, 1);
+ qint64 haveRead = socket->read(&c, 1);
+ if (haveRead < 0) {
+ return -1; // FIXME
+ }
+ bytes += haveRead;
fragment.append(c);
}
}
+
return bytes;
}
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 365308f..de6c0d2 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -125,10 +125,15 @@ public:
qint64 bytesAvailable() const;
qint64 bytesAvailableNextBlock() const;
+ bool readAnyAvailable() const;
QByteArray readAny();
QByteArray readAll();
void setDownstreamLimited(bool t);
+ bool supportsUserProvidedDownloadBuffer();
+ void setUserProvidedDownloadBuffer(char*);
+ char* userProvidedDownloadBuffer();
+
bool isFinished() const;
bool isPipeliningUsed() const;
@@ -150,6 +155,7 @@ Q_SIGNALS:
void finished();
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
void headerChanged();
+ // FIXME we need to change this to qint64!
void dataReadProgress(int done, int total);
void dataSendProgress(qint64 done, qint64 total);
void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
@@ -175,15 +181,16 @@ public:
qint64 readHeader(QAbstractSocket *socket);
void parseHeader(const QByteArray &header);
qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
+ qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
bool findChallenge(bool forProxy, QByteArray &challenge) const;
QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
void clear();
void clearHttpLayerInformation();
- qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
- qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
- qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
+ qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
+ qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
void appendUncompressedReplyData(QByteArray &qba);
void appendUncompressedReplyData(QByteDataBuffer &data);
@@ -213,6 +220,7 @@ public:
} state;
QHttpNetworkRequest request;
+ bool ssl;
int statusCode;
int majorVersion;
int minorVersion;
@@ -242,6 +250,8 @@ public:
bool pipeliningUsed;
bool downstreamLimited;
+
+ char* userProvidedDownloadBuffer;
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 2ae119e..cbc0d97 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -63,6 +63,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
pipeliningAllowed = other.pipeliningAllowed;
customVerb = other.customVerb;
withCredentials = other.withCredentials;
+ ssl = other.ssl;
}
QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
@@ -73,6 +74,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
{
return QHttpNetworkHeaderPrivate::operator==(other)
&& (operation == other.operation)
+ && (ssl == other.ssl)
&& (uploadByteDevice == other.uploadByteDevice);
}
@@ -156,8 +158,10 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request
}
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.headerField("content-type").isEmpty()) {
+ qWarning("content-type missing in HTTP POST, defaulting to application/octet-stream");
+ ba += "Content-Type: application/octet-stream\r\n";
+ }
if (!request.d->uploadByteDevice && request.d->url.hasQuery()) {
QByteArray query = request.d->url.encodedQuery();
ba += "Content-Length: ";
@@ -199,6 +203,15 @@ void QHttpNetworkRequest::setUrl(const QUrl &url)
d->url = url;
}
+bool QHttpNetworkRequest::isSsl() const
+{
+ return d->ssl;
+}
+void QHttpNetworkRequest::setSsl(bool s)
+{
+ d->ssl = s;
+}
+
qint64 QHttpNetworkRequest::contentLength() const
{
return d->contentLength();
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index ea68c8d4..4000eca 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -116,6 +116,9 @@ public:
bool withCredentials() const;
void setWithCredentials(bool b);
+ bool isSsl() const;
+ void setSsl(bool);
+
void setUploadByteDevice(QNonContiguousByteDevice *bd);
QNonContiguousByteDevice* uploadByteDevice() const;
@@ -146,6 +149,7 @@ public:
bool autoDecompress;
bool pipeliningAllowed;
bool withCredentials;
+ bool ssl;
};
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
new file mode 100644
index 0000000..092fa7d
--- /dev/null
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -0,0 +1,582 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHTTPTHREADDELEGATE_DEBUG
+#include "qhttpthreaddelegate_p.h"
+
+#include <QThread>
+#include <QTimer>
+#include <QAuthenticator>
+#include <QEventLoop>
+
+#include "private/qhttpnetworkreply_p.h"
+#include "private/qnetworkaccesscache_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
+{
+ QNetworkReply::NetworkError code;
+ // we've got an error
+ switch (httpStatusCode) {
+ case 401: // Authorization required
+ code = QNetworkReply::AuthenticationRequiredError;
+ break;
+
+ case 403: // Access denied
+ code = QNetworkReply::ContentOperationNotPermittedError;
+ break;
+
+ case 404: // Not Found
+ code = QNetworkReply::ContentNotFoundError;
+ break;
+
+ case 405: // Method Not Allowed
+ code = QNetworkReply::ContentOperationNotPermittedError;
+ break;
+
+ case 407:
+ code = QNetworkReply::ProxyAuthenticationRequiredError;
+ break;
+
+ case 418: // I'm a teapot
+ code = QNetworkReply::ProtocolInvalidOperationError;
+ break;
+
+
+ default:
+ if (httpStatusCode > 500) {
+ // some kind of server error
+ code = QNetworkReply::ProtocolUnknownError;
+ } else if (httpStatusCode >= 400) {
+ // content error we did not handle above
+ code = QNetworkReply::UnknownContentError;
+ } else {
+ qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
+ httpStatusCode, qPrintable(url.toString()));
+ code = QNetworkReply::ProtocolFailure;
+ }
+ }
+
+ return code;
+}
+
+
+static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
+{
+ QByteArray result;
+ QUrl copy = url;
+ bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
+ copy.setPort(copy.port(isEncrypted ? 443 : 80));
+ result = copy.toEncoded(QUrl::RemoveUserInfo | QUrl::RemovePath |
+ QUrl::RemoveQuery | QUrl::RemoveFragment);
+
+#ifndef QT_NO_NETWORKPROXY
+ if (proxy && proxy->type() != QNetworkProxy::NoProxy) {
+ QUrl key;
+
+ switch (proxy->type()) {
+ case QNetworkProxy::Socks5Proxy:
+ key.setScheme(QLatin1String("proxy-socks5"));
+ break;
+
+ case QNetworkProxy::HttpProxy:
+ case QNetworkProxy::HttpCachingProxy:
+ key.setScheme(QLatin1String("proxy-http"));
+ break;
+
+ default:
+ break;
+ }
+
+ if (!key.scheme().isEmpty()) {
+ key.setUserName(proxy->user());
+ key.setHost(proxy->hostName());
+ key.setPort(proxy->port());
+ key.setEncodedQuery(result);
+ result = key.toEncoded();
+ }
+ }
+#else
+ Q_UNUSED(proxy)
+#endif
+
+ return "http-connection:" + result;
+}
+
+class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
+ public QNetworkAccessCache::CacheableObject
+{
+ // Q_OBJECT
+public:
+#ifdef QT_NO_BEARERMANAGEMENT
+ QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
+ : QHttpNetworkConnection(hostName, port, encrypt)
+#else
+ QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt, QSharedPointer<QNetworkSession> networkSession)
+ : QHttpNetworkConnection(hostName, port, encrypt, /*parent=*/0, networkSession)
+#endif
+ {
+ setExpires(true);
+ setShareable(true);
+ }
+
+ virtual void dispose()
+ {
+#if 0 // sample code; do this right with the API
+ Q_ASSERT(!isWorking());
+#endif
+ delete this;
+ }
+};
+
+
+QThreadStorage<QNetworkAccessCache *> QHttpThreadDelegate::connections;
+
+
+QHttpThreadDelegate::~QHttpThreadDelegate()
+{
+ // It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply
+ if (httpReply) {
+ delete httpReply;
+ }
+
+ // Get the object cache that stores our QHttpNetworkConnection objects
+ // and release the entry for this QHttpNetworkConnection
+ if (connections.hasLocalData() && !cacheKey.isEmpty()) {
+ connections.localData()->releaseEntry(cacheKey);
+ }
+}
+
+
+QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
+ QObject(parent)
+ , ssl(false)
+ , downloadBufferMaximumSize(0)
+ , pendingDownloadData(0)
+ , pendingDownloadProgress(0)
+ , synchronous(false)
+ , incomingStatusCode(0)
+ , isPipeliningUsed(false)
+ , incomingContentLength(-1)
+ , incomingErrorCode(QNetworkReply::NoError)
+ , downloadBuffer(0)
+ , httpConnection(0)
+ , httpReply(0)
+ , synchronousRequestLoop(0)
+{
+}
+
+// This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread
+void QHttpThreadDelegate::startRequestSynchronously()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId();
+#endif
+ synchronous = true;
+
+ QEventLoop synchronousRequestLoop;
+ this->synchronousRequestLoop = &synchronousRequestLoop;
+
+ // Worst case timeout
+ QTimer::singleShot(30*1000, this, SLOT(abortRequest()));
+
+ QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection);
+ synchronousRequestLoop.exec();
+
+ connections.localData()->releaseEntry(cacheKey);
+ connections.setLocalData(0);
+
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
+#endif
+}
+
+
+// This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread
+void QHttpThreadDelegate::startRequest()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId();
+#endif
+ // Check QThreadStorage for the QNetworkAccessCache
+ // If not there, create this connection cache
+ if (!connections.hasLocalData()) {
+ connections.setLocalData(new QNetworkAccessCache());
+ }
+
+ // check if we have an open connection to this host
+ QUrl urlCopy = httpRequest.url();
+ urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
+
+#ifndef QT_NO_NETWORKPROXY
+ if (transparentProxy.type() != QNetworkProxy::NoProxy)
+ cacheKey = makeCacheKey(urlCopy, &transparentProxy);
+ else if (cacheProxy.type() != QNetworkProxy::NoProxy)
+ cacheKey = makeCacheKey(urlCopy, &cacheProxy);
+ else
+#endif
+ cacheKey = makeCacheKey(urlCopy, 0);
+
+
+ // the http object is actually a QHttpNetworkConnection
+ httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
+ if (httpConnection == 0) {
+ // no entry in cache; create an object
+ // the http object is actually a QHttpNetworkConnection
+#ifdef QT_NO_BEARERMANAGEMENT
+ httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
+#else
+ httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);
+#endif
+#ifndef QT_NO_OPENSSL
+ // Set the QSslConfiguration from this QNetworkRequest.
+ if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {
+ httpConnection->setSslConfiguration(incomingSslConfiguration);
+ }
+#endif
+
+#ifndef QT_NO_NETWORKPROXY
+ httpConnection->setTransparentProxy(transparentProxy);
+ httpConnection->setCacheProxy(cacheProxy);
+#endif
+
+ // cache the QHttpNetworkConnection corresponding to this cache key
+ connections.localData()->addEntry(cacheKey, httpConnection);
+ }
+
+
+ // Send the request to the connection
+ httpReply = httpConnection->sendRequest(httpRequest);
+ httpReply->setParent(this);
+
+ // Connect the reply signals that we need to handle and then forward
+ if (synchronous) {
+ connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
+ connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
+ connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),
+ this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
+
+ connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
+ connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
+
+ // Don't care about ignored SSL errors for now in the synchronous HTTP case.
+ } else if (!synchronous) {
+ connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
+ connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
+ connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),
+ this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
+ // some signals are only interesting when normal asynchronous style is used
+ connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
+ connect(httpReply,SIGNAL(dataReadProgress(int, int)), this, SLOT(dataReadProgressSlot(int,int)));
+ connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
+ this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
+#ifndef QT_NO_OPENSSL
+ connect(httpReply,SIGNAL(sslErrors(const QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
+#endif
+
+ // In the asynchronous HTTP case we can just forward those signals
+ // Connect the reply signals that we can directly forward
+ connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
+ connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+ }
+}
+
+// This gets called from the user thread or by the synchronous HTTP timeout timer
+void QHttpThreadDelegate::abortRequest()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
+#endif
+ if (httpReply) {
+ delete httpReply;
+ httpReply = 0;
+ }
+
+ // Got aborted by the timeout timer
+ if (synchronous) {
+ incomingErrorCode = QNetworkReply::TimeoutError;
+ QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
+ } else {
+ //only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
+ this->deleteLater();
+ }
+}
+
+void QHttpThreadDelegate::readyReadSlot()
+{
+ // Don't do in zerocopy case
+ if (!downloadBuffer.isNull())
+ return;
+
+ while (httpReply->readAnyAvailable()) {
+ pendingDownloadData->fetchAndAddRelease(1);
+ emit downloadData(httpReply->readAny());
+ }
+}
+
+void QHttpThreadDelegate::finishedSlot()
+{
+ if (!httpReply) {
+ qWarning() << "QHttpThreadDelegate::finishedSlot: HTTP reply had already been deleted, internal problem. Please report.";
+ return;
+ }
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
+#endif
+
+ // If there is still some data left emit that now
+ while (httpReply->readAnyAvailable()) {
+ pendingDownloadData->fetchAndAddRelease(1);
+ emit downloadData(httpReply->readAny());
+ }
+
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+#endif
+
+ if (httpReply->statusCode() >= 400) {
+ // it's an error reply
+ QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error downloading %1 - server replied: %2"));
+ msg = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase());
+ emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
+ }
+
+ emit downloadFinished();
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+void QHttpThreadDelegate::synchronousFinishedSlot()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
+#endif
+ if (httpReply->statusCode() >= 400) {
+ // it's an error reply
+ QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error downloading %1 - server replied: %2"));
+ incomingErrorDetail = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase());
+ incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
+ }
+
+ synchronousDownloadData = httpReply->readAll();
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
+{
+ if (!httpReply) {
+ qWarning() << "QHttpThreadDelegate::finishedWithErrorSlot: HTTP reply had already been deleted, internal problem. Please report.";
+ return;
+ }
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
+#endif
+
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+#endif
+ emit error(errorCode,detail);
+ emit downloadFinished();
+
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+
+void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
+#endif
+ incomingErrorCode = errorCode;
+ incomingErrorDetail = detail;
+
+ QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
+ httpReply = 0;
+}
+
+static void downloadBufferDeleter(char *ptr)
+{
+ delete[] ptr;
+}
+
+void QHttpThreadDelegate::headerChangedSlot()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
+#endif
+
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+#endif
+
+ // Is using a zerocopy buffer allowed by user and possible with this reply?
+ if (httpReply->supportsUserProvidedDownloadBuffer()
+ && downloadBufferMaximumSize > 0) {
+ char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
+ if (buf) {
+ downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
+ httpReply->setUserProvidedDownloadBuffer(buf);
+ }
+ }
+
+ // We fetch this into our own
+ incomingHeaders = httpReply->header();
+ incomingStatusCode = httpReply->statusCode();
+ incomingReasonPhrase = httpReply->reasonPhrase();
+ isPipeliningUsed = httpReply->isPipeliningUsed();
+ incomingContentLength = httpReply->contentLength();
+
+ emit downloadMetaData(incomingHeaders,
+ incomingStatusCode,
+ incomingReasonPhrase,
+ isPipeliningUsed,
+ downloadBuffer,
+ incomingContentLength);
+}
+
+void QHttpThreadDelegate::synchronousHeaderChangedSlot()
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
+#endif
+ // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
+ incomingHeaders = httpReply->header();
+ incomingStatusCode = httpReply->statusCode();
+ incomingReasonPhrase = httpReply->reasonPhrase();
+ isPipeliningUsed = httpReply->isPipeliningUsed();
+ incomingContentLength = httpReply->contentLength();
+}
+
+
+void QHttpThreadDelegate::dataReadProgressSlot(int done, int total)
+{
+ // If we don't have a download buffer don't attempt to go this codepath
+ // It is not used by QNetworkAccessHttpBackend
+ if (downloadBuffer.isNull())
+ return;
+
+ pendingDownloadProgress->fetchAndAddRelease(1);
+ emit downloadProgress(done, total);
+}
+
+void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
+{
+ authenticationManager->cacheCredentials(request.url(), authenticator);
+}
+
+
+#ifndef QT_NO_OPENSSL
+void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
+{
+ emit sslConfigurationChanged(httpReply->sslConfiguration());
+
+ bool ignoreAll = false;
+ QList<QSslError> specificErrors;
+ emit sslErrors(errors, &ignoreAll, &specificErrors);
+ if (ignoreAll)
+ httpReply->ignoreSslErrors();
+ if (!specificErrors.isEmpty())
+ httpReply->ignoreSslErrors(specificErrors);
+}
+#endif
+
+void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
+{
+ Q_UNUSED(request);
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
+#endif
+
+ // Ask the credential cache
+ QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
+ if (!credential.isNull()) {
+ a->setUser(credential.user);
+ a->setPassword(credential.password);
+ }
+
+ // Disconnect this connection now since we only want to ask the authentication cache once.
+ QObject::disconnect(this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
+{
+#ifdef QHTTPTHREADDELEGATE_DEBUG
+ qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
+#endif
+ // Ask the credential cache
+ QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
+ if (!credential.isNull()) {
+ a->setUser(credential.user);
+ a->setPassword(credential.password);
+ }
+
+ // Disconnect this connection now since we only want to ask the authentication cache once.
+ QObject::disconnect(this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
+}
+
+#endif
+
+#endif // QT_NO_HTTP
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
new file mode 100644
index 0000000..5730956
--- /dev/null
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPTHREADDELEGATE_H
+#define QHTTPTHREADDELEGATE_H
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QObject>
+#include <QThreadStorage>
+#include <QNetworkProxy>
+#include <QSslConfiguration>
+#include <QSslError>
+#include <QList>
+#include <QNetworkReply>
+#include "qhttpnetworkrequest_p.h"
+#include "qhttpnetworkconnection_p.h"
+#include <QSharedPointer>
+#include "qsslconfiguration.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QHttpNetworkReply;
+class QEventLoop;
+class QNetworkAccessCache;
+class QNetworkAccessCachedHttpConnection;
+
+class QHttpThreadDelegate : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QHttpThreadDelegate(QObject *parent = 0);
+
+ ~QHttpThreadDelegate();
+
+ // incoming
+ bool ssl;
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration incomingSslConfiguration;
+#endif
+ QHttpNetworkRequest httpRequest;
+ qint64 downloadBufferMaximumSize;
+ // From backend, modified by us for signal compression
+ QSharedPointer<QAtomicInt> pendingDownloadData;
+ QSharedPointer<QAtomicInt> pendingDownloadProgress;
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy cacheProxy;
+ QNetworkProxy transparentProxy;
+#endif
+ QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
+ bool synchronous;
+
+ // outgoing, Retrieved in the synchronous HTTP case
+ QByteArray synchronousDownloadData;
+ QList<QPair<QByteArray,QByteArray> > incomingHeaders;
+ int incomingStatusCode;
+ QString incomingReasonPhrase;
+ bool isPipeliningUsed;
+ qint64 incomingContentLength;
+ QNetworkReply::NetworkError incomingErrorCode;
+ QString incomingErrorDetail;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
+
+protected:
+ // The zerocopy download buffer, if used:
+ QSharedPointer<char> downloadBuffer;
+ // The QHttpNetworkConnection that is used
+ QNetworkAccessCachedHttpConnection *httpConnection;
+ QByteArray cacheKey;
+ QHttpNetworkReply *httpReply;
+
+ // Used for implementing the synchronous HTTP, see startRequestSynchronously()
+ QEventLoop *synchronousRequestLoop;
+
+signals:
+ void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *);
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *);
+#endif
+#ifndef QT_NO_OPENSSL
+ void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
+ void sslConfigurationChanged(const QSslConfiguration);
+#endif
+ void downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void downloadProgress(qint64, qint64);
+ void downloadData(QByteArray);
+ void error(QNetworkReply::NetworkError, const QString);
+ void downloadFinished();
+public slots:
+ // This are called via QueuedConnection from user thread
+ void startRequest();
+ void abortRequest();
+ // This is called with a BlockingQueuedConnection from user thread
+ void startRequestSynchronously();
+protected slots:
+ // From QHttp*
+ void readyReadSlot();
+ void finishedSlot();
+ void finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
+ void synchronousFinishedSlot();
+ void synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
+ void headerChangedSlot();
+ void synchronousHeaderChangedSlot();
+ void dataReadProgressSlot(int done, int total);
+ void cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
+#ifndef QT_NO_OPENSSL
+ void sslErrorsSlot(const QList<QSslError> &errors);
+#endif
+
+ void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *);
+#ifndef QT_NO_NETWORKPROXY
+ void synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &, QAuthenticator *);
+#endif
+
+protected:
+ // Cache for all the QHttpNetworkConnection objects.
+ // This is per thread.
+ static QThreadStorage<QNetworkAccessCache *> connections;
+
+};
+
+// This QNonContiguousByteDevice is connected to the QNetworkAccessHttpBackend
+// and represents the PUT/POST data.
+class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevice
+{
+ Q_OBJECT
+protected:
+ bool wantDataPending;
+ qint64 m_amount;
+ char *m_data;
+ QByteArray m_dataArray;
+ bool m_atEnd;
+ qint64 m_size;
+public:
+ QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
+ : QNonContiguousByteDevice(),
+ wantDataPending(false),
+ m_amount(0),
+ m_data(0),
+ m_atEnd(aE),
+ m_size(s)
+ {
+ }
+
+ ~QNonContiguousByteDeviceThreadForwardImpl()
+ {
+ }
+
+ const char* readPointer(qint64 maximumLength, qint64 &len)
+ {
+ if (m_amount == 0 && wantDataPending == false) {
+ len = 0;
+ wantDataPending = true;
+ emit wantData(maximumLength);
+ } else if (m_amount == 0 && wantDataPending == true) {
+ // Do nothing, we already sent a wantData signal and wait for results
+ len = 0;
+ } else if (m_amount > 0) {
+ len = m_amount;
+ return m_data;
+ }
+ // cannot happen
+ return 0;
+ }
+
+ bool advanceReadPointer(qint64 a)
+ {
+ if (m_data == 0)
+ return false;
+
+ m_amount -= a;
+ m_data += a;
+
+ // To main thread to inform about our state
+ emit processedData(a);
+
+ // FIXME possible optimization, already ask user thread for some data
+
+ return true;
+ }
+
+ bool atEnd()
+ {
+ if (m_amount > 0)
+ return false;
+ else
+ return m_atEnd;
+ }
+
+ bool reset()
+ {
+ m_amount = 0;
+ m_data = 0;
+
+ // Communicate as BlockingQueuedConnection
+ bool b = false;
+ emit resetData(&b);
+ return b;
+ }
+
+ qint64 size()
+ {
+ return m_size;
+ }
+
+public slots:
+ // From user thread:
+ void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
+ {
+ wantDataPending = false;
+
+ m_dataArray = dataArray;
+ m_data = const_cast<char*>(m_dataArray.constData());
+ m_amount = dataArray.size();
+
+ m_atEnd = dataAtEnd;
+ m_size = dataSize;
+
+ // This will tell the HTTP code (QHttpNetworkConnectionChannel) that we have data available now
+ emit readyRead();
+ }
+
+signals:
+ // void readyRead(); in parent class
+ // void readProgress(qint64 current, qint64 total); happens in the main thread with the real bytedevice
+
+ // to main thread:
+ void wantData(qint64);
+ void processedData(qint64);
+ void resetData(bool *b);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif // QHTTPTHREADDELEGATE_H
diff --git a/src/network/access/qnetworkaccessauthenticationmanager.cpp b/src/network/access/qnetworkaccessauthenticationmanager.cpp
new file mode 100644
index 0000000..1b15cf9
--- /dev/null
+++ b/src/network/access/qnetworkaccessauthenticationmanager.cpp
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessmanager.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
+
+#include "QtCore/qbuffer.h"
+#include "QtCore/qurl.h"
+#include "QtCore/qvector.h"
+#include "QtCore/QMutexLocker"
+#include "QtNetwork/qauthenticator.h"
+
+QT_BEGIN_NAMESPACE
+
+
+
+
+class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
+ public QNetworkAccessCache::CacheableObject
+{
+public:
+ QNetworkAuthenticationCache()
+ {
+ setExpires(false);
+ setShareable(true);
+ reserve(1);
+ }
+
+ QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
+ {
+ iterator it = qLowerBound(begin(), end(), domain);
+ if (it == end() && !isEmpty())
+ --it;
+ if (it == end() || !domain.startsWith(it->domain))
+ return 0;
+ return &*it;
+ }
+
+ void insert(const QString &domain, const QString &user, const QString &password)
+ {
+ QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
+ if (closestMatch && closestMatch->domain == domain) {
+ // we're overriding the current credentials
+ closestMatch->user = user;
+ closestMatch->password = password;
+ } else {
+ QNetworkAuthenticationCredential newCredential;
+ newCredential.domain = domain;
+ newCredential.user = user;
+ newCredential.password = password;
+
+ if (closestMatch)
+ QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
+ else
+ QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
+ }
+ }
+
+ virtual void dispose() { delete this; }
+};
+
+#ifndef QT_NO_NETWORKPROXY
+static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
+{
+ QUrl key;
+
+ switch (proxy.type()) {
+ case QNetworkProxy::Socks5Proxy:
+ key.setScheme(QLatin1String("proxy-socks5"));
+ break;
+
+ case QNetworkProxy::HttpProxy:
+ case QNetworkProxy::HttpCachingProxy:
+ key.setScheme(QLatin1String("proxy-http"));
+ break;
+
+ case QNetworkProxy::FtpCachingProxy:
+ key.setScheme(QLatin1String("proxy-ftp"));
+ break;
+
+ case QNetworkProxy::DefaultProxy:
+ case QNetworkProxy::NoProxy:
+ // shouldn't happen
+ return QByteArray();
+
+ // no default:
+ // let there be errors if a new proxy type is added in the future
+ }
+
+ if (key.scheme().isEmpty())
+ // proxy type not handled
+ return QByteArray();
+
+ key.setUserName(proxy.user());
+ key.setHost(proxy.hostName());
+ key.setPort(proxy.port());
+ key.setFragment(realm);
+ return "auth:" + key.toEncoded();
+}
+#endif
+
+static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
+{
+ QUrl copy = url;
+ copy.setFragment(realm);
+ return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
+}
+
+
+#ifndef QT_NO_NETWORKPROXY
+void QNetworkAccessAuthenticationManager::cacheProxyCredentials(const QNetworkProxy &p,
+ const QAuthenticator *authenticator)
+{
+ Q_ASSERT(authenticator);
+ Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
+ Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
+
+ QMutexLocker mutexLocker(&mutex);
+
+ QString realm = authenticator->realm();
+ QNetworkProxy proxy = p;
+ proxy.setUser(authenticator->user());
+ // Set two credentials: one with the username and one without
+ do {
+ // Set two credentials actually: one with and one without the realm
+ do {
+ QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
+ if (cacheKey.isEmpty())
+ return; // should not happen
+
+ QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
+ auth->insert(QString(), authenticator->user(), authenticator->password());
+ authenticationCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
+
+ if (realm.isEmpty()) {
+ break;
+ } else {
+ realm.clear();
+ }
+ } while (true);
+
+ if (proxy.user().isEmpty())
+ break;
+ else
+ proxy.setUser(QString());
+ } while (true);
+}
+
+QNetworkAuthenticationCredential
+QNetworkAccessAuthenticationManager::fetchCachedProxyCredentials(const QNetworkProxy &p,
+ const QAuthenticator *authenticator)
+{
+ QNetworkProxy proxy = p;
+ if (proxy.type() == QNetworkProxy::DefaultProxy) {
+ proxy = QNetworkProxy::applicationProxy();
+ }
+ if (!proxy.password().isEmpty())
+ return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
+
+ QString realm;
+ if (authenticator)
+ realm = authenticator->realm();
+
+ QMutexLocker mutexLocker(&mutex);
+ QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
+ if (cacheKey.isEmpty())
+ return QNetworkAuthenticationCredential();
+ if (!authenticationCache.hasEntry(cacheKey))
+ return QNetworkAuthenticationCredential();
+
+ QNetworkAuthenticationCache *auth =
+ static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
+ QNetworkAuthenticationCredential cred = *auth->findClosestMatch(QString());
+ authenticationCache.releaseEntry(cacheKey);
+
+ // proxy cache credentials always have exactly one item
+ Q_ASSERT_X(!cred.isNull(), "QNetworkAccessManager",
+ "Internal inconsistency: found a cache key for a proxy, but it's empty");
+ return cred;
+}
+
+#endif
+
+void QNetworkAccessAuthenticationManager::cacheCredentials(const QUrl &url,
+ const QAuthenticator *authenticator)
+{
+ Q_ASSERT(authenticator);
+ QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
+ QString realm = authenticator->realm();
+
+ QMutexLocker mutexLocker(&mutex);
+
+ // Set two credentials actually: one with and one without the username in the URL
+ QUrl copy = url;
+ copy.setUserName(authenticator->user());
+ do {
+ QByteArray cacheKey = authenticationKey(copy, realm);
+ if (authenticationCache.hasEntry(cacheKey)) {
+ QNetworkAuthenticationCache *auth =
+ static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
+ auth->insert(domain, authenticator->user(), authenticator->password());
+ authenticationCache.releaseEntry(cacheKey);
+ } else {
+ QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
+ auth->insert(domain, authenticator->user(), authenticator->password());
+ authenticationCache.addEntry(cacheKey, auth);
+ }
+
+ if (copy.userName().isEmpty()) {
+ break;
+ } else {
+ copy.setUserName(QString());
+ }
+ } while (true);
+}
+
+/*!
+ Fetch the credential data from the credential cache.
+
+ If auth is 0 (as it is when called from createRequest()), this will try to
+ look up with an empty realm. That fails in most cases for HTTP (because the
+ realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
+ never sends the credentials on the first attempt: it needs to find out what
+ authentication methods the server supports.
+
+ For FTP, realm is always empty.
+*/
+QNetworkAuthenticationCredential
+QNetworkAccessAuthenticationManager::fetchCachedCredentials(const QUrl &url,
+ const QAuthenticator *authentication)
+{
+ if (!url.password().isEmpty())
+ return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
+
+ QString realm;
+ if (authentication)
+ realm = authentication->realm();
+
+ QByteArray cacheKey = authenticationKey(url, realm);
+
+ QMutexLocker mutexLocker(&mutex);
+ if (!authenticationCache.hasEntry(cacheKey))
+ return QNetworkAuthenticationCredential();
+
+ QNetworkAuthenticationCache *auth =
+ static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
+ QNetworkAuthenticationCredential cred = *auth->findClosestMatch(url.path());
+ authenticationCache.releaseEntry(cacheKey);
+ return cred;
+}
+
+void QNetworkAccessAuthenticationManager::clearCache()
+{
+ authenticationCache.clear();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
new file mode 100644
index 0000000..ddfc116
--- /dev/null
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
+#define QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworkaccessmanager.h"
+#include "qnetworkaccesscache_p.h"
+#include "qnetworkaccessbackend_p.h"
+#include "QtNetwork/qnetworkproxy.h"
+#include "QtCore/QMutex"
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QAbstractNetworkCache;
+class QNetworkAuthenticationCredential;
+class QNetworkCookieJar;
+
+class QNetworkAuthenticationCredential
+{
+public:
+ QString domain;
+ QString user;
+ QString password;
+ bool isNull() {
+ return domain.isNull();
+ }
+};
+Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
+inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
+{ return t1.domain < t2; }
+
+class QNetworkAccessAuthenticationManager
+{
+public:
+ QNetworkAccessAuthenticationManager() { };
+
+ void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
+ QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url,
+ const QAuthenticator *auth = 0);
+
+#ifndef QT_NO_NETWORKPROXY
+ void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
+ QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy,
+ const QAuthenticator *auth = 0);
+#endif
+
+ void clearCache();
+
+protected:
+ QNetworkAccessCache authenticationCache;
+ QMutex mutex;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index d2d96bc..2fae7d6 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -41,12 +41,13 @@
#include "qnetworkaccessbackend_p.h"
#include "qnetworkaccessmanager_p.h"
+#include "qnetworkconfigmanager.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "QtCore/qhash.h"
#include "QtCore/qmutex.h"
-#include "QtNetwork/qnetworksession.h"
+#include "QtNetwork/private/qnetworksession_p.h"
#include "qnetworkaccesscachebackend_p.h"
#include "qabstractnetworkcache.h"
@@ -106,12 +107,10 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM
QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
{
- QNonContiguousByteDevice* device = 0;
-
if (reply->outgoingDataBuffer)
- device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer);
+ uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer));
else if (reply->outgoingData) {
- device = QNonContiguousByteDeviceFactory::create(reply->outgoingData);
+ uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingData));
} else {
return 0;
}
@@ -120,21 +119,20 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
QVariant(false)) == QVariant(true);
if (bufferDisallowed)
- device->disableReset();
-
- // make sure we delete this later
- device->setParent(this);
+ uploadByteDevice->disableReset();
- connect(device, SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
+ // We want signal emissions only for normal asynchronous uploads
+ if (!isSynchronous())
+ connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
- return device;
+ return uploadByteDevice.data();
}
// need to have this function since the reply is a private member variable
// and the special backends need to access this.
void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
{
- if (reply->isFinished())
+ if (reply->isFinished)
return;
reply->emitUploadProgress(bytesSent, bytesTotal);
}
@@ -241,6 +239,17 @@ void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
reply->appendDownstreamData(data);
}
+// not actually appending data, it was already written to the user buffer
+void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+{
+ reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
+}
+
+char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
+{
+ return reply->getDownloadBuffer(size);
+}
+
QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
{
return reply->q_func()->header(header);
@@ -316,11 +325,6 @@ void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator
manager->authenticationRequired(this, authenticator);
}
-void QNetworkAccessBackend::cacheCredentials(QAuthenticator *authenticator)
-{
- manager->cacheCredentials(this->reply->url, authenticator);
-}
-
void QNetworkAccessBackend::metaDataChanged()
{
reply->metaDataChanged();
@@ -340,8 +344,6 @@ void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
#endif
}
-#ifndef QT_NO_BEARERMANAGEMENT
-
/*!
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
@@ -349,29 +351,62 @@ void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
*/
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;
+#ifndef QT_NO_BEARERMANAGEMENT
+ // For bearer, check if session start is required
+ if (manager->networkSession) {
+ // session required
+ if (manager->networkSession->isOpen() &&
+ manager->networkSession->state() == QNetworkSession::Connected) {
+ // Session is already open and ready to use.
+ // copy network session down to the backend
+ setProperty("_q_networksession", QVariant::fromValue(manager->networkSession));
+ } else {
+ // Session not ready, but can skip for loopback connections
+
+ // 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.
+ } else {
+ // need to wait for session to be opened
+ return false;
+ }
+ }
}
+#endif
- if (manager->networkSession->isOpen() &&
- manager->networkSession->state() == QNetworkSession::Connected) {
- open();
- return true;
+#ifndef QT_NO_NETWORKPROXY
+#ifndef QT_NO_BEARERMANAGEMENT
+ // Get the proxy settings from the network session (in the case of service networks,
+ // the proxy settings change depending which AP was activated)
+ QNetworkSession *session = manager->networkSession.data();
+ QNetworkConfiguration config;
+ if (session) {
+ QNetworkConfigurationManager configManager;
+ // The active configuration tells us what IAP is in use
+ QVariant v = session->sessionProperty(QLatin1String("ActiveConfiguration"));
+ if (v.isValid())
+ config = configManager.configurationFromIdentifier(qvariant_cast<QString>(v));
+ // Fallback to using the configuration if no active configuration
+ if (!config.isValid())
+ config = session->configuration();
+ // or unspecified configuration if that is no good either
+ if (!config.isValid())
+ config = QNetworkConfiguration();
}
+ reply->proxyList = manager->queryProxy(QNetworkProxyQuery(config, url()));
+#else // QT_NO_BEARERMANAGEMENT
+ // Without bearer management, the proxy depends only on the url
+ reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
+#endif
+#endif
- return false;
+ // now start the request
+ open();
+ return true;
}
-#endif
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 45be26e..ff05306 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -70,7 +70,6 @@ class QNetworkAccessManagerPrivate;
class QNetworkReplyImplPrivate;
class QAbstractNetworkCache;
class QNetworkCacheMetaData;
-class QNetworkAccessBackendUploadIODevice;
class QNonContiguousByteDevice;
// Should support direct file upload from disk or download to disk.
@@ -175,12 +174,17 @@ protected:
// Create the device used for reading the upload data
QNonContiguousByteDevice* createUploadByteDevice();
-
// these functions control the downstream mechanism
// that is, data that has come via the connection and is going out the backend
qint64 nextDownstreamBlockSize() const;
void writeDownstreamData(QByteDataBuffer &list);
+ // not actually appending data, it was already written to the user buffer
+ void writeDownstreamDataDownloadBuffer(qint64, qint64);
+ char* getDownloadBuffer(qint64);
+
+ QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
+
public slots:
// for task 251801, needs to be a slot to be called asynchronously
void writeDownstreamData(QIODevice *data);
@@ -192,19 +196,22 @@ protected slots:
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
#endif
void authenticationRequired(QAuthenticator *auth);
- void cacheCredentials(QAuthenticator *auth);
void metaDataChanged();
void redirectionRequested(const QUrl &destination);
void sslErrors(const QList<QSslError> &errors);
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
+protected:
+ // FIXME In the long run we should get rid of our QNAM architecture
+ // and scrap this ReplyImpl/Backend distinction.
+ QNetworkAccessManagerPrivate *manager;
+ QNetworkReplyImplPrivate *reply;
+
private:
friend class QNetworkAccessManager;
friend class QNetworkAccessManagerPrivate;
- friend class QNetworkAccessBackendUploadIODevice;
friend class QNetworkReplyImplPrivate;
- QNetworkAccessManagerPrivate *manager;
- QNetworkReplyImplPrivate *reply;
+
bool synchronous;
};
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 37dbf04..1881dac 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -66,6 +66,7 @@ void QNetworkAccessCacheBackend::open()
QString msg = QCoreApplication::translate("QNetworkAccessCacheBackend", "Error opening %1")
.arg(this->url().toString());
error(QNetworkReply::ContentNotFoundError, msg);
+ } else {
setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
}
finished();
@@ -85,14 +86,18 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
QNetworkCacheMetaData::AttributesMap attributes = item.attributes();
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
- setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
// set the raw headers
QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
+ for ( ; it != end; ++it) {
+ if (it->first.toLower() == "cache-control" &&
+ it->second.toLower().contains("must-revalidate")) {
+ return false;
+ }
setRawHeader(it->first, it->second);
+ }
// handle a possible redirect
QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp
deleted file mode 100644
index dcd5e98..0000000
--- a/src/network/access/qnetworkaccessdatabackend.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2011 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$
-** GNU Lesser General Public License Usage
-** 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.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qnetworkaccessdatabackend_p.h"
-#include "qnetworkrequest.h"
-#include "qnetworkreply.h"
-#include "qurlinfo.h"
-#include "private/qdataurl_p.h"
-#include <qcoreapplication.h>
-
-QT_BEGIN_NAMESPACE
-
-QNetworkAccessBackend *
-QNetworkAccessDataBackendFactory::create(QNetworkAccessManager::Operation,
- const QNetworkRequest &request) const
-{
- if (request.url().scheme() == QLatin1String("data"))
- return new QNetworkAccessDataBackend;
-
- return 0;
-}
-
-QNetworkAccessDataBackend::QNetworkAccessDataBackend()
-{
-}
-
-QNetworkAccessDataBackend::~QNetworkAccessDataBackend()
-{
-}
-
-void QNetworkAccessDataBackend::open()
-{
- QUrl uri = request().url();
-
- if (operation() != QNetworkAccessManager::GetOperation &&
- operation() != QNetworkAccessManager::HeadOperation) {
- // data: doesn't support anything but GET
- const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
- "Operation not supported on %1")
- .arg(uri.toString());
- error(QNetworkReply::ContentOperationNotPermittedError, msg);
- 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
- const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
- "Invalid URI: %1").arg(uri.toString());
- error(QNetworkReply::ProtocolFailure, msg);
- finished();
-}
-
-void QNetworkAccessDataBackend::closeDownstreamChannel()
-{
-}
-
-void QNetworkAccessDataBackend::closeUpstreamChannel()
-{
-}
-
-bool QNetworkAccessDataBackend::waitForDownstreamReadyRead(int)
-{
- return false;
-}
-
-bool QNetworkAccessDataBackend::waitForUpstreamBytesWritten(int)
-{
- return false;
-}
-
-bool QNetworkAccessDataBackend::processRequestSynchronously()
-{
-#ifndef QT_NO_BEARERMANAGEMENT
- start();
-#else
- open();
-#endif
- return true;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 2b2014b..7c4adca 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -65,11 +65,19 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("qrc") || !url.toLocalFile().isEmpty())
+ if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) {
return new QNetworkAccessFileBackend;
- else if (!url.isEmpty() && url.authority().isEmpty()) {
- // check if QFile could, in theory, open this URL
+ } else if (!url.isEmpty() && url.authority().isEmpty()) {
+ // check if QFile could, in theory, open this URL via the file engines
+ // it has to be in the format:
+ // prefix:path/to/file
+ // or prefix:/path/to/file
+ //
+ // this construct here must match the one below in open()
QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery));
+ // On Windows and Symbian the drive letter is detected as the scheme.
+ if (fi.exists() && (url.scheme().isEmpty() || (url.scheme().length() == 1)))
+ qWarning("QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files");
if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
return new QNetworkAccessFileBackend;
}
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
index 0a3eb7c..0461da2 100644
--- a/src/network/access/qnetworkaccessftpbackend.cpp
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -77,7 +77,7 @@ QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("ftp"))
+ if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0)
return new QNetworkAccessFtpBackend;
return 0;
}
@@ -153,6 +153,10 @@ void QNetworkAccessFtpBackend::open()
if (!objectCache->requestEntry(cacheKey, this,
SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) {
ftp = new QNetworkAccessCachedFtpConnection;
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the QFtp
+ ftp->setProperty("_q_networksession", property("_q_networksession"));
+#endif
#ifndef QT_NO_NETWORKPROXY
if (proxy.type() == QNetworkProxy::FtpCachingProxy)
ftp->setProxy(proxy.hostName(), proxy.port());
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index abef8ab..8a247ce 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -47,65 +47,25 @@
#include "qabstractnetworkcache.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
+#include "QtNetwork/private/qnetworksession_p.h"
#include "qnetworkrequest_p.h"
#include "qnetworkcookie_p.h"
#include "QtCore/qdatetime.h"
#include "QtCore/qelapsedtimer.h"
#include "QtNetwork/qsslconfiguration.h"
+#include "qhttpthreaddelegate_p.h"
+#include "qthread.h"
#ifndef QT_NO_HTTP
#include <string.h> // for strchr
-QT_BEGIN_NAMESPACE
+Q_DECLARE_METATYPE(QSharedPointer<char>)
-enum {
- DefaultHttpPort = 80,
- DefaultHttpsPort = 443
-};
+QT_BEGIN_NAMESPACE
class QNetworkProxy;
-static QByteArray makeCacheKey(QNetworkAccessHttpBackend *backend, QNetworkProxy *proxy)
-{
- QByteArray result;
- QUrl copy = backend->url();
- bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
- copy.setPort(copy.port(isEncrypted ? DefaultHttpsPort : DefaultHttpPort));
- result = copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath |
- QUrl::RemoveQuery | QUrl::RemoveFragment);
-
-#ifndef QT_NO_NETWORKPROXY
- if (proxy->type() != QNetworkProxy::NoProxy) {
- QUrl key;
-
- switch (proxy->type()) {
- case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
- break;
-
- case QNetworkProxy::HttpProxy:
- case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
- break;
-
- default:
- break;
- }
-
- if (!key.scheme().isEmpty()) {
- key.setUserName(proxy->user());
- key.setHost(proxy->hostName());
- key.setPort(proxy->port());
- key.setEncodedQuery(result);
- result = key.toEncoded();
- }
- }
-#endif
-
- return "http-connection:" + result;
-}
-
static inline bool isSeparator(register char c)
{
static const char separators[] = "()<>@,;:\\\"/[]?={}";
@@ -230,71 +190,13 @@ QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op,
return 0;
}
-static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
-{
- QNetworkReply::NetworkError code;
- // we've got an error
- switch (httpStatusCode) {
- case 401: // Authorization required
- code = QNetworkReply::AuthenticationRequiredError;
- break;
-
- case 403: // Access denied
- code = QNetworkReply::ContentOperationNotPermittedError;
- break;
-
- case 404: // Not Found
- code = QNetworkReply::ContentNotFoundError;
- break;
-
- case 405: // Method Not Allowed
- code = QNetworkReply::ContentOperationNotPermittedError;
- break;
-
- case 407:
- code = QNetworkReply::ProxyAuthenticationRequiredError;
- break;
-
- default:
- if (httpStatusCode > 500) {
- // some kind of server error
- code = QNetworkReply::ProtocolUnknownError;
- } else if (httpStatusCode >= 400) {
- // content error we did not handle above
- code = QNetworkReply::UnknownContentError;
- } else {
- qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
- httpStatusCode, qPrintable(url.toString()));
- code = QNetworkReply::ProtocolFailure;
- }
- }
-
- return code;
-}
-
-class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
- public QNetworkAccessCache::CacheableObject
-{
- // Q_OBJECT
-public:
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
- : QHttpNetworkConnection(hostName, port, encrypt)
- {
- setExpires(true);
- setShareable(true);
- }
-
- virtual void dispose()
- {
-#if 0 // sample code; do this right with the API
- Q_ASSERT(!isWorking());
-#endif
- delete this;
- }
-};
-
QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
- : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0)
+ : QNetworkAccessBackend()
+ , statusCode(0)
+ , pendingDownloadDataEmissions(new QAtomicInt())
+ , pendingDownloadProgressEmissions(new QAtomicInt())
+ , loadingFromCache(false)
+ , usingZerocopyDownloadBuffer(false)
#ifndef QT_NO_OPENSSL
, pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false)
#endif
@@ -304,77 +206,49 @@ QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend()
{
- if (http)
- disconnectFromHttp();
+ // This will do nothing if the request was already finished or aborted
+ emit abortHttpRequest();
+
#ifndef QT_NO_OPENSSL
delete pendingSslConfiguration;
#endif
}
-void QNetworkAccessHttpBackend::disconnectFromHttp()
-{
- if (http) {
- // This is abut disconnecting signals, not about disconnecting TCP connections
- disconnect(http, 0, this, 0);
-
- // Get the object cache that stores our QHttpNetworkConnection objects
- QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this);
-
- // synchronous calls are not put into the cache, so for them the key is empty
- if (!cacheKey.isEmpty())
- cache->releaseEntry(cacheKey);
- }
-
- // This is abut disconnecting signals, not about disconnecting TCP connections
- if (httpReply)
- disconnect(httpReply, 0, this, 0);
-
- http = 0;
- httpReply = 0;
- cacheKey.clear();
-}
-
-void QNetworkAccessHttpBackend::finished()
-{
- if (http)
- disconnectFromHttp();
- // call parent
- QNetworkAccessBackend::finished();
-}
-
/*
For a given httpRequest
1) If AlwaysNetwork, return
2) If we have a cache entry for this url populate headers so the server can return 304
3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
*/
-void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache)
+bool QNetworkAccessHttpBackend::loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest)
{
QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
(QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
- // forced reload from the network
- // tell any caching proxy servers to reload too
- httpRequest.setHeaderField("Cache-Control", "no-cache");
- httpRequest.setHeaderField("Pragma", "no-cache");
- return;
+ // If the request does not already specify preferred cache-control
+ // force reload from the network and tell any caching proxy servers to reload too
+ if (!request().rawHeaderList().contains("Cache-Control")) {
+ httpRequest.setHeaderField("Cache-Control", "no-cache");
+ httpRequest.setHeaderField("Pragma", "no-cache");
+ }
+ return false;
}
// The disk cache API does not currently support partial content retrieval.
// That is why we don't use the disk cache for any such requests.
if (request().hasRawHeader("Range"))
- return;
+ return false;
QAbstractNetworkCache *nc = networkCache();
if (!nc)
- return; // no local cache
+ return false; // no local cache
QNetworkCacheMetaData metaData = nc->metaData(url());
if (!metaData.isValid())
- return; // not in cache
+ return false; // not in cache
if (!metaData.saveToDisk())
- return;
+ return false;
QNetworkHeadersPrivate cacheHeaders;
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
@@ -388,13 +262,11 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
if (lastModified.isValid())
httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
- if (CacheLoadControlAttribute == QNetworkRequest::PreferNetwork) {
- it = cacheHeaders.findRawHeader("Cache-Control");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
- return;
- }
+ it = cacheHeaders.findRawHeader("Cache-Control");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+ if (cacheControl.contains("must-revalidate"))
+ return false;
}
QDateTime currentDateTime = QDateTime::currentDateTime();
@@ -465,14 +337,12 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
#endif
if (!response_is_fresh)
- return;
+ return false;
- loadedFromCache = true;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
#endif
- if (!sendCacheContents(metaData))
- loadedFromCache = false;
+ return sendCacheContents(metaData);
}
static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio)
@@ -490,30 +360,103 @@ static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& pr
void QNetworkAccessHttpBackend::postRequest()
{
+ QThread *thread = 0;
+ if (isSynchronous()) {
+ // A synchronous HTTP request uses its own thread
+ thread = new QThread();
+ QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread->start();
+ } else if (!manager->httpThread) {
+ // We use the manager-global thread.
+ // At some point we could switch to having multiple threads if it makes sense.
+ manager->httpThread = new QThread();
+ QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater()));
+ manager->httpThread->start();
+#ifndef QT_NO_NETWORKPROXY
+ qRegisterMetaType<QNetworkProxy>("QNetworkProxy");
+#endif
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
+ qRegisterMetaType<QSslConfiguration>("QSslConfiguration");
+#endif
+ qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >");
+ qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest");
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
+ qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>");
+
+ thread = manager->httpThread;
+ } else {
+ // Asynchronous request, thread already exists
+ thread = manager->httpThread;
+ }
+
+ QUrl url = request().url();
+ httpRequest.setUrl(url);
+
+ bool ssl = url.scheme().toLower() == QLatin1String("https");
+ setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
+ httpRequest.setSsl(ssl);
+
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy transparentProxy, cacheProxy;
+
+ foreach (const QNetworkProxy &p, proxyList()) {
+ // use the first proxy that works
+ // for non-encrypted connections, any transparent or HTTP proxy
+ // for encrypted, only transparent proxies
+ if (!ssl
+ && (p.capabilities() & QNetworkProxy::CachingCapability)
+ && (p.type() == QNetworkProxy::HttpProxy ||
+ p.type() == QNetworkProxy::HttpCachingProxy)) {
+ cacheProxy = p;
+ transparentProxy = QNetworkProxy::NoProxy;
+ break;
+ }
+ if (p.isTransparentProxy()) {
+ transparentProxy = p;
+ cacheProxy = QNetworkProxy::NoProxy;
+ break;
+ }
+ }
+
+ // check if at least one of the proxies
+ if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
+ cacheProxy.type() == QNetworkProxy::DefaultProxy) {
+ // unsuitable proxies
+ QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
+ Q_ARG(QString, tr("No suitable proxy found")));
+ QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection);
+ return;
+ }
+#endif
+
+
bool loadedFromCache = false;
- QHttpNetworkRequest httpRequest;
httpRequest.setPriority(convert(request().priority()));
+
switch (operation()) {
case QNetworkAccessManager::GetOperation:
httpRequest.setOperation(QHttpNetworkRequest::Get);
- validateCache(httpRequest, loadedFromCache);
+ loadedFromCache = loadFromCacheIfAllowed(httpRequest);
break;
case QNetworkAccessManager::HeadOperation:
httpRequest.setOperation(QHttpNetworkRequest::Head);
- validateCache(httpRequest, loadedFromCache);
+ loadedFromCache = loadFromCacheIfAllowed(httpRequest);
break;
case QNetworkAccessManager::PostOperation:
invalidateCache();
httpRequest.setOperation(QHttpNetworkRequest::Post);
- httpRequest.setUploadByteDevice(createUploadByteDevice());
+ createUploadByteDevice();
break;
case QNetworkAccessManager::PutOperation:
invalidateCache();
httpRequest.setOperation(QHttpNetworkRequest::Put);
- httpRequest.setUploadByteDevice(createUploadByteDevice());
+ createUploadByteDevice();
break;
case QNetworkAccessManager::DeleteOperation:
@@ -524,7 +467,7 @@ void QNetworkAccessHttpBackend::postRequest()
case QNetworkAccessManager::CustomOperation:
invalidateCache(); // for safety reasons, we don't know what the operation does
httpRequest.setOperation(QHttpNetworkRequest::Custom);
- httpRequest.setUploadByteDevice(createUploadByteDevice());
+ createUploadByteDevice();
httpRequest.setCustomVerb(request().attribute(
QNetworkRequest::CustomVerbAttribute).toByteArray());
break;
@@ -533,7 +476,12 @@ void QNetworkAccessHttpBackend::postRequest()
break; // can't happen
}
- httpRequest.setUrl(url());
+ if (loadedFromCache) {
+ // commented this out since it will be called later anyway
+ // by copyFinished()
+ //QNetworkAccessBackend::finished();
+ return; // no need to send the request! :)
+ }
QList<QByteArray> headers = request().rawHeaderList();
if (resumeOffset != 0) {
@@ -558,16 +506,10 @@ void QNetworkAccessHttpBackend::postRequest()
httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
}
}
+
foreach (const QByteArray &header, headers)
httpRequest.setHeaderField(header, request().rawHeader(header));
- if (loadedFromCache) {
- // commented this out since it will be called later anyway
- // by copyFinished()
- //QNetworkAccessBackend::finished();
- return; // no need to send the request! :)
- }
-
if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
httpRequest.setPipeliningAllowed(true);
@@ -576,195 +518,223 @@ void QNetworkAccessHttpBackend::postRequest()
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
httpRequest.setWithCredentials(false);
- httpReply = http->sendRequest(httpRequest);
- httpReply->setParent(this);
-#ifndef QT_NO_OPENSSL
- if (pendingSslConfiguration)
- httpReply->setSslConfiguration(*pendingSslConfiguration);
- if (pendingIgnoreAllSslErrors)
- httpReply->ignoreSslErrors();
- httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList);
- connect(httpReply, SIGNAL(sslErrors(QList<QSslError>)),
- SLOT(sslErrors(QList<QSslError>)));
-#endif
- connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));
- connect(httpReply, SIGNAL(finished()), SLOT(replyFinished()));
- connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
- SLOT(httpError(QNetworkReply::NetworkError,QString)));
- connect(httpReply, SIGNAL(headerChanged()), SLOT(replyHeaderChanged()));
- connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
- SLOT(httpCacheCredentials(QHttpNetworkRequest,QAuthenticator*)));
-#ifndef QT_NO_NETWORKPROXY
- connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
- SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+ // Create the HTTP thread delegate
+ QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
+#ifndef Q_NO_BEARERMANAGEMENT
+ QVariant v(property("_q_networksession"));
+ if (v.isValid())
+ delegate->networkSession = qvariant_cast<QSharedPointer<QNetworkSession> >(v);
#endif
- connect(httpReply, SIGNAL(authenticationRequired(const QHttpNetworkRequest,QAuthenticator*)),
- SLOT(httpAuthenticationRequired(const QHttpNetworkRequest,QAuthenticator*)));
-}
-
-void QNetworkAccessHttpBackend::invalidateCache()
-{
- QAbstractNetworkCache *nc = networkCache();
- if (nc)
- nc->remove(url());
-}
-
-void QNetworkAccessHttpBackend::open()
-{
- QUrl url = request().url();
- bool encrypt = url.scheme().toLower() == QLatin1String("https");
- setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, encrypt);
- // set the port number in the reply if it wasn't set
- url.setPort(url.port(encrypt ? DefaultHttpsPort : DefaultHttpPort));
+ // For the synchronous HTTP, this is the normal way the delegate gets deleted
+ // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
+ connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
- QNetworkProxy *theProxy = 0;
+ // Set the properties it needs
+ delegate->httpRequest = httpRequest;
#ifndef QT_NO_NETWORKPROXY
- QNetworkProxy transparentProxy, cacheProxy;
+ delegate->cacheProxy = cacheProxy;
+ delegate->transparentProxy = transparentProxy;
+#endif
+ delegate->ssl = ssl;
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ delegate->incomingSslConfiguration = request().sslConfiguration();
+#endif
- foreach (const QNetworkProxy &p, proxyList()) {
- // use the first proxy that works
- // for non-encrypted connections, any transparent or HTTP proxy
- // for encrypted, only transparent proxies
- if (!encrypt
- && (p.capabilities() & QNetworkProxy::CachingCapability)
- && (p.type() == QNetworkProxy::HttpProxy ||
- p.type() == QNetworkProxy::HttpCachingProxy)) {
- cacheProxy = p;
- transparentProxy = QNetworkProxy::NoProxy;
- theProxy = &cacheProxy;
- break;
+ // Do we use synchronous HTTP?
+ delegate->synchronous = isSynchronous();
+
+ // The authentication manager is used to avoid the BlockingQueuedConnection communication
+ // from HTTP thread to user thread in some cases.
+ delegate->authenticationManager = manager->authenticationManager;
+
+ if (!isSynchronous()) {
+ // Tell our zerocopy policy to the delegate
+ delegate->downloadBufferMaximumSize =
+ request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();
+
+ // These atomic integers are used for signal compression
+ delegate->pendingDownloadData = pendingDownloadDataEmissions;
+ delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;
+
+ // Connect the signals of the delegate to us
+ connect(delegate, SIGNAL(downloadData(QByteArray)),
+ this, SLOT(replyDownloadData(QByteArray)),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(downloadFinished()),
+ this, SLOT(replyFinished()),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+ this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
+ this, SLOT(replyDownloadProgressSlot(qint64,qint64)),
+ Qt::QueuedConnection);
+ connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),
+ this, SLOT(httpError(QNetworkReply::NetworkError, const QString)),
+ Qt::QueuedConnection);
+#ifndef QT_NO_OPENSSL
+ connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
+ this, SLOT(replySslConfigurationChanged(QSslConfiguration)),
+ Qt::QueuedConnection);
+#endif
+ // Those need to report back, therefire BlockingQueuedConnection
+ connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+ Qt::BlockingQueuedConnection);
+#ifndef QT_NO_NETWORKPROXY
+ connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ Qt::BlockingQueuedConnection);
+#endif
+#ifndef QT_NO_OPENSSL
+ connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
+ this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
+ Qt::BlockingQueuedConnection);
+#endif
+ // This signal we will use to start the request.
+ connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
+ connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
+
+ if (uploadByteDevice) {
+ QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
+ new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size());
+ if (uploadByteDevice->isResetDisabled())
+ forwardUploadDevice->disableReset();
+ forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()
+ delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
+
+ // From main thread to user thread:
+ QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)),
+ forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection);
+ QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
+ forwardUploadDevice, SIGNAL(readyRead()),
+ Qt::QueuedConnection);
+
+ // From http thread to user thread:
+ QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
+ this, SLOT(wantUploadDataSlot(qint64)));
+ QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
+ this, SLOT(sentUploadDataSlot(qint64)));
+ connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
+ this, SLOT(resetUploadDataSlot(bool*)),
+ Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
}
- if (p.isTransparentProxy()) {
- transparentProxy = p;
- cacheProxy = QNetworkProxy::NoProxy;
- theProxy = &transparentProxy;
- break;
+ } else if (isSynchronous()) {
+ connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
+
+ if (uploadByteDevice) {
+ // For the synchronous HTTP use case the use thread (this one here) is blocked
+ // so we cannot use the asynchronous upload architecture.
+ // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
+ // use the uploadByteDevice provided to us by the QNetworkReplyImpl.
+ // The code that is in QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread
+ // since it only wraps a QRingBuffer
+ delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
}
}
- // check if at least one of the proxies
- if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
- cacheProxy.type() == QNetworkProxy::DefaultProxy) {
- // unsuitable proxies
- if (isSynchronous()) {
- error(QNetworkReply::ProxyNotFoundError, tr("No suitable proxy found"));
- finished();
+
+ // Move the delegate to the http thread
+ delegate->moveToThread(thread);
+ // This call automatically moves the uploadDevice too for the asynchronous case.
+
+ // Send an signal to the delegate so it starts working in the other thread
+ if (isSynchronous()) {
+ emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
+
+ if (delegate->incomingErrorCode != QNetworkReply::NoError) {
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength);
+ replyDownloadData(delegate->synchronousDownloadData);
+ httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
} else {
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
- Q_ARG(QString, tr("No suitable proxy found")));
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength);
+ replyDownloadData(delegate->synchronousDownloadData);
}
- return;
- }
-#endif
- if (isSynchronous()) {
- // for synchronous requests, we just create a new connection
- http = new QHttpNetworkConnection(1, url.host(), url.port(), encrypt, this);
-#ifndef QT_NO_NETWORKPROXY
- http->setTransparentProxy(transparentProxy);
- http->setCacheProxy(cacheProxy);
-#endif
- postRequest();
- processRequestSynchronously();
+ // End the thread. It will delete itself from the finished() signal
+ thread->quit();
+
+ finished();
} else {
- // check if we have an open connection to this host
- cacheKey = makeCacheKey(this, theProxy);
- QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this);
- // the http object is actually a QHttpNetworkConnection
- http = static_cast<QNetworkAccessCachedHttpConnection *>(cache->requestEntryNow(cacheKey));
- if (http == 0) {
- // no entry in cache; create an object
- // the http object is actually a QHttpNetworkConnection
- http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt);
+ emit startHttpRequest(); // Signal to the HTTP thread and go back to user.
+ }
+}
-#ifndef QT_NO_NETWORKPROXY
- http->setTransparentProxy(transparentProxy);
- http->setCacheProxy(cacheProxy);
-#endif
+void QNetworkAccessHttpBackend::invalidateCache()
+{
+ QAbstractNetworkCache *nc = networkCache();
+ if (nc)
+ nc->remove(url());
+}
- // cache the QHttpNetworkConnection corresponding to this cache key
- cache->addEntry(cacheKey, static_cast<QNetworkAccessCachedHttpConnection *>(http.data()));
- }
- postRequest();
- }
+void QNetworkAccessHttpBackend::open()
+{
+ postRequest();
}
void QNetworkAccessHttpBackend::closeDownstreamChannel()
{
- // this indicates that the user closed the stream while the reply isn't finished yet
+ // FIXME Maybe we can get rid of this whole architecture part
}
void QNetworkAccessHttpBackend::downstreamReadyWrite()
{
- readFromHttp();
- if (httpReply && httpReply->bytesAvailable() == 0 && httpReply->isFinished())
- replyFinished();
+ // FIXME Maybe we can get rid of this whole architecture part
}
void QNetworkAccessHttpBackend::setDownstreamLimited(bool b)
{
- if (httpReply)
- httpReply->setDownstreamLimited(b);
+ Q_UNUSED(b);
+ // We know that readBuffer maximum size limiting is broken since quite a while.
+ // The task to fix this is QTBUG-15065
}
-void QNetworkAccessHttpBackend::replyReadyRead()
+void QNetworkAccessHttpBackend::replyDownloadData(QByteArray d)
{
- readFromHttp();
-}
-
-void QNetworkAccessHttpBackend::readFromHttp()
-{
- if (!httpReply)
+ int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1;
+
+ if (pendingSignals > 0) {
+ // Some more signal emissions to this slot are pending.
+ // Instead of writing the downstream data, we wait
+ // and do it in the next call we get
+ // (signal comppression)
+ pendingDownloadData.append(d);
return;
-
- // We read possibly more than nextDownstreamBlockSize(), but
- // this is not a critical thing since it is already in the
- // memory anyway
-
- QByteDataBuffer list;
-
- while (httpReply->bytesAvailable() != 0 && nextDownstreamBlockSize() != 0 && nextDownstreamBlockSize() > list.byteAmount()) {
- list.append(httpReply->readAny());
}
- if (!list.isEmpty())
- writeDownstreamData(list);
+ pendingDownloadData.append(d);
+ d.clear();
+ // We need to usa a copy for calling writeDownstreamData as we could
+ // possibly recurse into this this function when we call
+ // appendDownstreamDataSignalEmissions because the user might call
+ // processEvents() or spin an event loop when this occur.
+ QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData;
+ pendingDownloadData.clear();
+ writeDownstreamData(pendingDownloadDataCopy);
}
void QNetworkAccessHttpBackend::replyFinished()
{
- if (httpReply->bytesAvailable())
- // we haven't read everything yet. Wait some more.
+ // We are already loading from cache, we still however
+ // got this signal because it was posted already
+ if (loadingFromCache)
return;
- int statusCode = httpReply->statusCode();
- if (statusCode >= 400) {
- // it's an error reply
- QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
- "Error downloading %1 - server replied: %2"));
- msg = msg.arg(url().toString(), httpReply->reasonPhrase());
- error(statusCodeFromHttp(httpReply->statusCode(), httpReply->url()), msg);
- }
-
-#ifndef QT_NO_OPENSSL
- // store the SSL configuration now
- // once we call finished(), we won't have access to httpReply anymore
- QSslConfiguration sslConfig = httpReply->sslConfiguration();
- if (pendingSslConfiguration) {
- *pendingSslConfiguration = sslConfig;
- } else if (!sslConfig.isNull()) {
- QT_TRY {
- pendingSslConfiguration = new QSslConfiguration(sslConfig);
- } QT_CATCH(...) {
- qWarning("QNetworkAccess: could not allocate a QSslConfiguration object for a SSL connection.");
- }
- }
-#endif
-
finished();
}
@@ -786,12 +756,27 @@ void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode)
}
}
-void QNetworkAccessHttpBackend::replyHeaderChanged()
+void QNetworkAccessHttpBackend::replyDownloadMetaData
+ (QList<QPair<QByteArray,QByteArray> > hm,
+ int sc,QString rp,bool pu,
+ QSharedPointer<char> db,
+ qint64 contentLength)
{
- setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, httpReply->isPipeliningUsed());
+ statusCode = sc;
+ reasonPhrase = rp;
+
+ // Download buffer
+ if (!db.isNull()) {
+ reply->setDownloadBuffer(db, contentLength);
+ usingZerocopyDownloadBuffer = true;
+ } else {
+ usingZerocopyDownloadBuffer = false;
+ }
+
+ setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
// reconstruct the HTTP header
- QList<QPair<QByteArray, QByteArray> > headerMap = httpReply->header();
+ QList<QPair<QByteArray, QByteArray> > headerMap = hm;
QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
end = headerMap.constEnd();
QByteArray header;
@@ -808,11 +793,10 @@ void QNetworkAccessHttpBackend::replyHeaderChanged()
setRawHeader(it->first, value);
}
- setAttribute(QNetworkRequest::HttpStatusCodeAttribute, httpReply->statusCode());
- setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase());
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
// is it a redirection?
- const int statusCode = httpReply->statusCode();
checkForRedirect(statusCode);
if (statusCode >= 500 && statusCode < 600) {
@@ -854,19 +838,29 @@ void QNetworkAccessHttpBackend::replyHeaderChanged()
if (!isCachingEnabled())
setCachingEnabled(true);
}
+
metaDataChanged();
}
-void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &,
- QAuthenticator *auth)
+void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received, qint64 total)
{
- authenticationRequired(auth);
+ // we can be sure here that there is a download buffer
+
+ int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
+ if (pendingSignals > 0) {
+ // Let's ignore this signal and look at the next one coming in
+ // (signal comppression)
+ return;
+ }
+
+ // Now do the actual notification of new bytes
+ writeDownstreamDataDownloadBuffer(received, total);
}
-void QNetworkAccessHttpBackend::httpCacheCredentials(const QHttpNetworkRequest &,
- QAuthenticator *auth)
+void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &,
+ QAuthenticator *auth)
{
- cacheCredentials(auth);
+ authenticationRequired(auth);
}
void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
@@ -875,8 +869,56 @@ void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "http error!" << errorCode << errorString;
#endif
+
error(errorCode, errorString);
- finished();
+}
+
+#ifndef QT_NO_OPENSSL
+void QNetworkAccessHttpBackend::replySslErrors(
+ const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
+{
+ // Go to generic backend
+ sslErrors(list);
+ // Check if the callback set any ignore and return this here to http thread
+ if (pendingIgnoreAllSslErrors)
+ *ignoreAll = true;
+ if (!pendingIgnoreSslErrorsList.isEmpty())
+ *toBeIgnored = pendingIgnoreSslErrorsList;
+}
+
+void QNetworkAccessHttpBackend::replySslConfigurationChanged(const QSslConfiguration &c)
+{
+ // Receiving the used SSL configuration from the HTTP thread
+ if (pendingSslConfiguration)
+ *pendingSslConfiguration = c;
+ else if (!c.isNull())
+ pendingSslConfiguration = new QSslConfiguration(c);
+}
+#endif
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkAccessHttpBackend::resetUploadDataSlot(bool *r)
+{
+ *r = uploadByteDevice->reset();
+}
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 amount)
+{
+ uploadByteDevice->advanceReadPointer(amount);
+}
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize)
+{
+ // call readPointer
+ qint64 currentUploadDataLength = 0;
+ char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
+ // Let's make a copy of this data
+ QByteArray dataArray(data, currentUploadDataLength);
+
+ // Communicate back to HTTP thread
+ emit haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
}
/*
@@ -919,16 +961,18 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m
// This needs to be emitted in the event loop because it can be reached at
// the direct code path of qnam.get(...) before the user has a chance
// to connect any signals.
- QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "metaDataChanged", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection);
qRegisterMetaType<QIODevice*>("QIODevice*");
- QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
+ QMetaObject::invokeMethod(this, "writeDownstreamData", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes";
#endif
- if (httpReply)
- disconnect(httpReply, SIGNAL(finished()), this, SLOT(replyFinished()));
+
+ // Set the following flag so we can ignore some signals from HTTP thread
+ // that would still come
+ loadingFromCache = true;
return true;
}
@@ -941,39 +985,29 @@ void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev)
#ifndef QT_NO_OPENSSL
void QNetworkAccessHttpBackend::ignoreSslErrors()
{
- if (httpReply)
- httpReply->ignoreSslErrors();
- else
- pendingIgnoreAllSslErrors = true;
+ pendingIgnoreAllSslErrors = true;
}
void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors)
{
- if (httpReply) {
- httpReply->ignoreSslErrors(errors);
- } else {
- // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
- // is called before QNetworkAccessManager::get() (or post(), etc.)
- pendingIgnoreSslErrorsList = errors;
- }
+ // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+ // is called before QNetworkAccessManager::get() (or post(), etc.)
+ pendingIgnoreSslErrorsList = errors;
}
void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const
{
- if (httpReply)
- config = httpReply->sslConfiguration();
- else if (pendingSslConfiguration)
+ if (pendingSslConfiguration)
config = *pendingSslConfiguration;
+ else
+ config = request().sslConfiguration();
}
void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig)
{
- if (httpReply)
- httpReply->setSslConfiguration(newconfig);
- else if (pendingSslConfiguration)
- *pendingSslConfiguration = newconfig;
- else
- pendingSslConfiguration = new QSslConfiguration(newconfig);
+ // Setting a SSL configuration on a reply is not supported. The user needs to set
+ // her/his QSslConfiguration on the QNetworkRequest.
+ Q_UNUSED(newconfig);
}
#endif
@@ -1075,7 +1109,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
bool canDiskCache;
// only cache GET replies by default, all other replies (POST, PUT, DELETE)
// are not cacheable by default (according to RFC 2616 section 9)
- if (httpReply->request().operation() == QHttpNetworkRequest::Get) {
+ if (httpRequest.operation() == QHttpNetworkRequest::Get) {
canDiskCache = true;
// 14.32
@@ -1093,7 +1127,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
canDiskCache = false;
// responses to POST might be cacheable
- } else if (httpReply->request().operation() == QHttpNetworkRequest::Post) {
+ } else if (httpRequest.operation() == QHttpNetworkRequest::Post) {
canDiskCache = false;
// some pages contain "expires:" and "cache-control: no-cache" field,
@@ -1107,12 +1141,11 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
}
metaData.setSaveToDisk(canDiskCache);
- int statusCode = httpReply->statusCode();
QNetworkCacheMetaData::AttributesMap attributes;
if (statusCode != 304) {
// update the status code
attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
- attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase());
+ attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
} else {
// this is a redirection, keep the attributes intact
attributes = oldMetaData.attributes();
@@ -1128,7 +1161,8 @@ bool QNetworkAccessHttpBackend::canResume() const
return false;
// Can only resume if server/resource supports Range header.
- if (httpReply->headerField("Accept-Ranges", "none") == "none")
+ QByteArray acceptRangesheaderName("Accept-Ranges");
+ if (!hasRawHeader(acceptRangesheaderName) || rawHeader(acceptRangesheaderName) == "none")
return false;
// We only support resuming for byte ranges.
@@ -1138,6 +1172,11 @@ bool QNetworkAccessHttpBackend::canResume() const
return false;
}
+ // If we're using a download buffer then we don't support resuming/migration
+ // right now. Too much trouble.
+ if (usingZerocopyDownloadBuffer)
+ return false;
+
return true;
}
@@ -1146,87 +1185,6 @@ void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset)
resumeOffset = offset;
}
-bool QNetworkAccessHttpBackend::processRequestSynchronously()
-{
- QHttpNetworkConnectionChannel *channel = &http->channels()[0];
-
- // Disconnect all socket signals. They will only confuse us when using waitFor*
- QObject::disconnect(channel->socket, 0, 0, 0);
-
- qint64 timeout = 20*1000; // 20 sec
- QElapsedTimer timeoutTimer;
-
- bool waitResult = channel->socket->waitForConnected(timeout);
- timeoutTimer.start();
-
- if (!waitResult || channel->socket->state() != QAbstractSocket::ConnectedState) {
- error(QNetworkReply::UnknownNetworkError, QLatin1String("could not connect"));
- return false;
- }
- channel->_q_connected(); // this will send the request (via sendRequest())
-
-#ifndef QT_NO_OPENSSL
- if (http->isSsl()) {
- qint64 remainingTimeEncrypted = timeout - timeoutTimer.elapsed();
- if (!static_cast<QSslSocket *>(channel->socket)->waitForEncrypted(remainingTimeEncrypted)) {
- error(QNetworkReply::SslHandshakeFailedError,
- QLatin1String("could not encrypt or timeout while encrypting"));
- return false;
- }
- channel->_q_encrypted();
- }
-#endif
-
- // if we get a 401 or 407, we might need to send the request twice, see below
- bool authenticating = false;
-
- do {
- channel->sendRequest();
-
- qint64 remainingTimeBytesWritten;
- while(channel->socket->bytesToWrite() > 0 ||
- channel->state == QHttpNetworkConnectionChannel::WritingState) {
- remainingTimeBytesWritten = timeout - timeoutTimer.elapsed();
- channel->sendRequest(); // triggers channel->socket->write()
- if (!channel->socket->waitForBytesWritten(remainingTimeBytesWritten)) {
- error(QNetworkReply::TimeoutError,
- QLatin1String("could not write bytes to socket or timeout while writing"));
- return false;
- }
- }
-
- qint64 remainingTimeBytesRead = timeout - timeoutTimer.elapsed();
- // Loop for at most remainingTime until either the socket disconnects
- // or the reply is finished
- do {
- waitResult = channel->socket->waitForReadyRead(remainingTimeBytesRead);
- remainingTimeBytesRead = timeout - timeoutTimer.elapsed();
- if (!waitResult || remainingTimeBytesRead <= 0
- || channel->socket->state() != QAbstractSocket::ConnectedState) {
- error(QNetworkReply::TimeoutError,
- QLatin1String("could not read from socket or timeout while reading"));
- return false;
- }
-
- if (channel->socket->bytesAvailable())
- channel->_q_readyRead();
-
- if (!httpReply)
- return false; // we got a 401 or 407 and cannot handle it (it might happen that
- // disconnectFromHttp() was called, in that case the reply is zero)
- // ### I am quite sure this does not work for NTLM
- // ### how about uploading to an auth / proxyAuth site?
-
- authenticating = (httpReply->statusCode() == 401 || httpReply->statusCode() == 407);
-
- if (httpReply->isFinished())
- break;
- } while (remainingTimeBytesRead > 0);
- } while (authenticating);
-
- return true;
-}
-
QT_END_NAMESPACE
#endif // QT_NO_HTTP
diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h
index affdd9c..7d4ea56 100644
--- a/src/network/access/qnetworkaccesshttpbackend_p.h
+++ b/src/network/access/qnetworkaccesshttpbackend_p.h
@@ -61,6 +61,8 @@
#include "QtCore/qpointer.h"
#include "QtCore/qdatetime.h"
+#include "QtCore/qsharedpointer.h"
+#include "qatomic.h"
#ifndef QT_NO_HTTP
@@ -99,23 +101,44 @@ public:
bool canResume() const;
void setResumeOffset(quint64 offset);
- virtual bool processRequestSynchronously();
+signals:
+ // To HTTP thread:
+ void startHttpRequest();
+ void abortHttpRequest();
+ void startHttpRequestSynchronously();
+
+ void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
private slots:
- void replyReadyRead();
+ // From HTTP thread:
+ void replyDownloadData(QByteArray);
void replyFinished();
- void replyHeaderChanged();
+ void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
- void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
+#ifndef QT_NO_OPENSSL
+ void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
+ void replySslConfigurationChanged(const QSslConfiguration&);
+#endif
+
+ // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
+ void resetUploadDataSlot(bool *r);
+ void wantUploadDataSlot(qint64);
+ void sentUploadDataSlot(qint64);
+
bool sendCacheContents(const QNetworkCacheMetaData &metaData);
- void finished(); // override
private:
- QHttpNetworkReply *httpReply;
- QPointer<QHttpNetworkConnection> http;
- QByteArray cacheKey;
- QNetworkAccessBackendUploadIODevice *uploadDevice;
+ QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread
+ int statusCode;
+ QString reasonPhrase;
+ // Will be increased by HTTP thread:
+ QSharedPointer<QAtomicInt> pendingDownloadDataEmissions;
+ QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions;
+ bool loadingFromCache;
+ QByteDataBuffer pendingDownloadData;
+ bool usingZerocopyDownloadBuffer;
#ifndef QT_NO_OPENSSL
QSslConfiguration *pendingSslConfiguration;
@@ -125,8 +148,7 @@ private:
quint64 resumeOffset;
- void disconnectFromHttp();
- void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache);
+ bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
void invalidateCache();
void postRequest();
void readFromHttp();
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 0433c34..8fc8eb7 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -53,10 +53,10 @@
#include "qnetworkaccesshttpbackend_p.h"
#include "qnetworkaccessftpbackend_p.h"
#include "qnetworkaccessfilebackend_p.h"
-#include "qnetworkaccessdatabackend_p.h"
#include "qnetworkaccessdebugpipebackend_p.h"
#include "qnetworkaccesscachebackend_p.h"
-#include "qfilenetworkreply_p.h"
+#include "qnetworkreplydataimpl_p.h"
+#include "qnetworkreplyfileimpl_p.h"
#include "QtCore/qbuffer.h"
#include "QtCore/qurl.h"
@@ -64,6 +64,10 @@
#include "QtNetwork/qauthenticator.h"
#include "QtNetwork/qsslconfiguration.h"
#include "QtNetwork/qnetworkconfigmanager.h"
+#include "QtNetwork/qhttpmultipart.h"
+#include "qhttpmultipart_p.h"
+
+#include "qthread.h"
QT_BEGIN_NAMESPACE
@@ -71,7 +75,6 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
#endif // QT_NO_HTTP
Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
-Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend)
#ifndef QT_NO_FTP
Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
#endif // QT_NO_FTP
@@ -85,7 +88,7 @@ static void ensureInitialized()
#ifndef QT_NO_HTTP
(void) httpBackend();
#endif // QT_NO_HTTP
- (void) dataBackend();
+
#ifndef QT_NO_FTP
(void) ftpBackend();
#endif
@@ -298,6 +301,10 @@ static void ensureInitialized()
again, without emitting the authenticationRequired() signal. If it
rejects the credentials, this signal will be emitted again.
+ \note It is not possible to use a QueuedConnection to connect to
+ this signal, as the connection will fail if the authenticator has
+ not been filled in with new information when the signal returns.
+
\sa proxyAuthenticationRequired()
*/
@@ -342,107 +349,6 @@ static void ensureInitialized()
QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
*/
-class QNetworkAuthenticationCredential
-{
-public:
- QString domain;
- QString user;
- QString password;
-};
-Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
-inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
-{ return t1.domain < t2; }
-
-class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
- public QNetworkAccessCache::CacheableObject
-{
-public:
- QNetworkAuthenticationCache()
- {
- setExpires(false);
- setShareable(true);
- reserve(1);
- }
-
- QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
- {
- iterator it = qLowerBound(begin(), end(), domain);
- if (it == end() && !isEmpty())
- --it;
- if (it == end() || !domain.startsWith(it->domain))
- return 0;
- return &*it;
- }
-
- void insert(const QString &domain, const QString &user, const QString &password)
- {
- QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
- if (closestMatch && closestMatch->domain == domain) {
- // we're overriding the current credentials
- closestMatch->user = user;
- closestMatch->password = password;
- } else {
- QNetworkAuthenticationCredential newCredential;
- newCredential.domain = domain;
- newCredential.user = user;
- newCredential.password = password;
-
- if (closestMatch)
- QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
- else
- QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
- }
- }
-
- virtual void dispose() { delete this; }
-};
-
-#ifndef QT_NO_NETWORKPROXY
-static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
-{
- QUrl key;
-
- switch (proxy.type()) {
- case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
- break;
-
- case QNetworkProxy::HttpProxy:
- case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
- break;
-
- case QNetworkProxy::FtpCachingProxy:
- key.setScheme(QLatin1String("proxy-ftp"));
- break;
-
- case QNetworkProxy::DefaultProxy:
- case QNetworkProxy::NoProxy:
- // shouldn't happen
- return QByteArray();
-
- // no default:
- // let there be errors if a new proxy type is added in the future
- }
-
- if (key.scheme().isEmpty())
- // proxy type not handled
- return QByteArray();
-
- key.setUserName(proxy.user());
- key.setHost(proxy.hostName());
- key.setPort(proxy.port());
- key.setFragment(realm);
- return "auth:" + key.toEncoded();
-}
-#endif
-
-static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
-{
- QUrl copy = url;
- copy.setFragment(realm);
- return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
-}
/*!
Constructs a QNetworkAccessManager object that is the center of
@@ -452,6 +358,8 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
: QObject(*new QNetworkAccessManagerPrivate, parent)
{
ensureInitialized();
+
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
}
/*!
@@ -727,6 +635,46 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const
}
/*!
+ \since 4.8
+
+ \overload
+
+ Sends the contents of the \a multiPart message to the destination
+ specified by \a request.
+
+ This can be used for sending MIME multipart messages over HTTP.
+
+ \sa QHttpMultiPart, QHttpPart, put()
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
+ QIODevice *device = multiPart->d_func()->device;
+ QNetworkReply *reply = post(newRequest, device);
+ return reply;
+}
+
+/*!
+ \since 4.8
+
+ \overload
+
+ Sends the contents of the \a multiPart message to the destination
+ specified by \a request.
+
+ This can be used for sending MIME multipart messages over HTTP.
+
+ \sa QHttpMultiPart, QHttpPart, post()
+*/
+QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
+ QIODevice *device = multiPart->d_func()->device;
+ QNetworkReply *reply = put(newRequest, device);
+ return reply;
+}
+
+/*!
Uploads the contents of \a data to the destination \a request and
returnes a new QNetworkReply object that will be open for reply.
@@ -752,7 +700,8 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODev
/*!
\overload
- Sends the contents of the \a data byte array to the destination
+
+ Sends the contents of the \a data byte array to the destination
specified by \a request.
*/
QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
@@ -946,30 +895,19 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
{
Q_D(QNetworkAccessManager);
- // 4.7 only hotfix fast path for data:// URLs
- // In 4.8 this is solved with QNetworkReplyDataImpl and will work there
- // This hotfix is done for not needing a QNetworkSession for data://
- if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
- && (req.url().scheme() == QLatin1String("data"))) {
- QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
- QNetworkReplyImplPrivate *priv = reply->d_func();
- priv->manager = this;
- priv->backend = new QNetworkAccessDataBackend();
- priv->backend->manager = this->d_func();
- priv->backend->setParent(reply);
- priv->backend->reply = priv;
- priv->setup(op, req, outgoingData);
- return reply;
- }
+ bool isLocalFile = req.url().isLocalFile();
+ QString scheme = req.url().scheme().toLower();
// fast path for GET on file:// URLs
- // Also if the scheme is empty we consider it a file.
// The QNetworkAccessFileBackend will right now only be used for PUT
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
- && (req.url().scheme() == QLatin1String("file")
- || req.url().scheme() == QLatin1String("qrc")
- || req.url().scheme().isEmpty())) {
- return new QFileNetworkReply(this, req, op);
+ && (isLocalFile || scheme == QLatin1String("qrc"))) {
+ return new QNetworkReplyFileImpl(this, req, op);
+ }
+
+ if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
+ && scheme == QLatin1String("data")) {
+ return new QNetworkReplyDataImpl(this, req, op);
}
// A request with QNetworkRequest::AlwaysCache does not need any bearer management
@@ -995,8 +933,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
#ifndef QT_NO_BEARERMANAGEMENT
// 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())) {
+ if (!d->networkAccessible && !isLocalFile) {
return new QDisabledNetworkReply(this, req, op);
}
@@ -1030,7 +967,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
if (d->cookieJar) {
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
if (!cookies.isEmpty())
- request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
+ request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
}
}
@@ -1038,7 +975,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
QUrl url = request.url();
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
#ifndef QT_NO_BEARERMANAGEMENT
- if (req.url().scheme() != QLatin1String("file") && !req.url().scheme().isEmpty()) {
+ if (!isLocalFile) {
connect(this, SIGNAL(networkSessionConnected()),
reply, SLOT(_q_networkSessionConnected()));
}
@@ -1053,10 +990,6 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
// third step: find a backend
priv->backend = d->findBackend(op, request);
-#ifndef QT_NO_NETWORKPROXY
- QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url()));
- priv->proxyList = proxyList;
-#endif
if (priv->backend) {
priv->backend->setParent(reply);
priv->backend->reply = priv;
@@ -1135,10 +1068,10 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend
// also called when last URL is empty, e.g. on first call
if (backend->reply->urlForLastAuthentication.isEmpty()
|| url != backend->reply->urlForLastAuthentication) {
- QNetworkAuthenticationCredential *cred = fetchCachedCredentials(url, authenticator);
- if (cred) {
- authenticator->setUser(cred->user);
- authenticator->setPassword(cred->password);
+ QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator);
+ if (!cred.isNull()) {
+ authenticator->setUser(cred.user);
+ authenticator->setPassword(cred.password);
backend->reply->urlForLastAuthentication = url;
return;
}
@@ -1151,7 +1084,7 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend
backend->reply->urlForLastAuthentication = url;
emit q->authenticationRequired(backend->reply->q_func(), authenticator);
- cacheCredentials(url, authenticator);
+ authenticationManager->cacheCredentials(url, authenticator);
}
#ifndef QT_NO_NETWORKPROXY
@@ -1168,10 +1101,10 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
// possible solution: some tracking inside the authenticator
// or a new function proxyAuthenticationSucceeded(true|false)
if (proxy != backend->reply->lastProxyAuthentication) {
- QNetworkAuthenticationCredential *cred = fetchCachedProxyCredentials(proxy);
- if (cred) {
- authenticator->setUser(cred->user);
- authenticator->setPassword(cred->password);
+ QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
+ if (!cred.isNull()) {
+ authenticator->setUser(cred.user);
+ authenticator->setPassword(cred.password);
return;
}
}
@@ -1183,75 +1116,7 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
backend->reply->lastProxyAuthentication = proxy;
emit q->proxyAuthenticationRequired(proxy, authenticator);
- cacheProxyCredentials(proxy, authenticator);
-}
-
-void QNetworkAccessManagerPrivate::cacheProxyCredentials(const QNetworkProxy &p,
- const QAuthenticator *authenticator)
-{
- Q_ASSERT(authenticator);
- Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
- Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
-
- QString realm = authenticator->realm();
- QNetworkProxy proxy = p;
- proxy.setUser(authenticator->user());
- // Set two credentials: one with the username and one without
- do {
- // Set two credentials actually: one with and one without the realm
- do {
- QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
- if (cacheKey.isEmpty())
- return; // should not happen
-
- QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
- auth->insert(QString(), authenticator->user(), authenticator->password());
- objectCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
-
- if (realm.isEmpty()) {
- break;
- } else {
- realm.clear();
- }
- } while (true);
-
- if (proxy.user().isEmpty())
- break;
- else
- proxy.setUser(QString());
- } while (true);
-}
-
-QNetworkAuthenticationCredential *
-QNetworkAccessManagerPrivate::fetchCachedProxyCredentials(const QNetworkProxy &p,
- const QAuthenticator *authenticator)
-{
- QNetworkProxy proxy = p;
- if (proxy.type() == QNetworkProxy::DefaultProxy) {
- proxy = QNetworkProxy::applicationProxy();
- }
- if (!proxy.password().isEmpty())
- return 0; // no need to set credentials if it already has them
-
- QString realm;
- if (authenticator)
- realm = authenticator->realm();
-
- QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
- if (cacheKey.isEmpty())
- return 0;
- if (!objectCache.hasEntry(cacheKey))
- return 0;
-
- QNetworkAuthenticationCache *auth =
- static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
- QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString());
- objectCache.releaseEntry(cacheKey);
-
- // proxy cache credentials always have exactly one item
- Q_ASSERT_X(cred, "QNetworkAccessManager",
- "Internal inconsistency: found a cache key for a proxy, but it's empty");
- return cred;
+ authenticationManager->cacheProxyCredentials(proxy, authenticator);
}
QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
@@ -1275,77 +1140,25 @@ QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProx
}
#endif
-void QNetworkAccessManagerPrivate::cacheCredentials(const QUrl &url,
- const QAuthenticator *authenticator)
-{
- Q_ASSERT(authenticator);
- QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
- QString realm = authenticator->realm();
-
- // Set two credentials actually: one with and one without the username in the URL
- QUrl copy = url;
- copy.setUserName(authenticator->user());
- do {
- QByteArray cacheKey = authenticationKey(copy, realm);
- if (objectCache.hasEntry(cacheKey)) {
- QNetworkAuthenticationCache *auth =
- static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
- auth->insert(domain, authenticator->user(), authenticator->password());
- objectCache.releaseEntry(cacheKey);
- } else {
- QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
- auth->insert(domain, authenticator->user(), authenticator->password());
- objectCache.addEntry(cacheKey, auth);
- }
-
- if (copy.userName().isEmpty()) {
- break;
- } else {
- copy.setUserName(QString());
- }
- } while (true);
-}
-
-/*!
- Fetch the credential data from the credential cache.
-
- If auth is 0 (as it is when called from createRequest()), this will try to
- look up with an empty realm. That fails in most cases for HTTP (because the
- realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
- never sends the credentials on the first attempt: it needs to find out what
- authentication methods the server supports.
-
- For FTP, realm is always empty.
-*/
-QNetworkAuthenticationCredential *
-QNetworkAccessManagerPrivate::fetchCachedCredentials(const QUrl &url,
- const QAuthenticator *authentication)
-{
- if (!url.password().isEmpty())
- return 0; // no need to set credentials if it already has them
-
- QString realm;
- if (authentication)
- realm = authentication->realm();
-
- QByteArray cacheKey = authenticationKey(url, realm);
- if (!objectCache.hasEntry(cacheKey))
- return 0;
-
- QNetworkAuthenticationCache *auth =
- static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
- QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
- objectCache.releaseEntry(cacheKey);
- return cred;
-}
-
void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
{
manager->d_func()->objectCache.clear();
+ manager->d_func()->authenticationManager->clearCache();
+
+ if (manager->d_func()->httpThread) {
+ // The thread will deleteLater() itself from its finished() signal
+ manager->d_func()->httpThread->quit();
+ manager->d_func()->httpThread = 0;
+ }
}
QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
{
+ if (httpThread) {
+ // The thread will deleteLater() itself from its finished() signal
+ httpThread->quit();
+ httpThread = 0;
+ }
}
#ifndef QT_NO_BEARERMANAGEMENT
@@ -1355,8 +1168,25 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
initializeSession = false;
- if (!config.isValid()) {
- networkSession.clear();
+ QSharedPointer<QNetworkSession> newSession;
+ if (config.isValid())
+ newSession = QSharedNetworkSessionManager::getSession(config);
+
+ if (networkSession) {
+ //do nothing if new and old session are the same
+ if (networkSession == newSession)
+ return;
+ //disconnect from old session
+ QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
+ QObject::disconnect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()));
+ QObject::disconnect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
+ q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
+ }
+
+ //switch to new session (null if config was invalid)
+ networkSession = newSession;
+
+ if (!networkSession) {
online = false;
if (networkAccessible == QNetworkAccessManager::NotAccessible)
@@ -1367,8 +1197,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
return;
}
- networkSession = QSharedNetworkSessionManager::getSession(config);
-
+ //connect to new session
QObject::connect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection);
//QueuedConnection is used to avoid deleting the networkSession inside its closed signal
QObject::connect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection);
@@ -1380,9 +1209,15 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
void QNetworkAccessManagerPrivate::_q_networkSessionClosed()
{
+ Q_Q(QNetworkAccessManager);
if (networkSession) {
networkConfiguration = networkSession->configuration().identifier();
+ //disconnect from old session
+ QObject::disconnect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected()));
+ QObject::disconnect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed()));
+ QObject::disconnect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
+ q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)));
networkSession.clear();
}
}
@@ -1391,8 +1226,12 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
{
Q_Q(QNetworkAccessManager);
- if (state == QNetworkSession::Connected)
+ //Do not emit the networkSessionConnected signal here, except for roaming -> connected
+ //transition, otherwise it is emitted twice in a row when opening a connection.
+ if (state == QNetworkSession::Connected && lastSessionState == QNetworkSession::Roaming)
emit q->networkSessionConnected();
+ lastSessionState = state;
+
if (online) {
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
online = false;
@@ -1407,6 +1246,54 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
}
#endif // QT_NO_BEARERMANAGEMENT
+QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart)
+{
+ // copy the request, we probably need to add some headers
+ QNetworkRequest newRequest(request);
+
+ // add Content-Type header if not there already
+ if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
+ QByteArray contentType;
+ contentType.reserve(34 + multiPart->d_func()->boundary.count());
+ contentType += "multipart/";
+ switch (multiPart->d_func()->contentType) {
+ case QHttpMultiPart::RelatedType:
+ contentType += "related";
+ break;
+ case QHttpMultiPart::FormDataType:
+ contentType += "form-data";
+ break;
+ case QHttpMultiPart::AlternativeType:
+ contentType += "alternative";
+ break;
+ default:
+ contentType += "mixed";
+ break;
+ }
+ // putting the boundary into quotes, recommended in RFC 2046 section 5.1.1
+ contentType += "; boundary=\"" + multiPart->d_func()->boundary + "\"";
+ newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
+ }
+
+ // add MIME-Version header if not there already (we must include the header
+ // if the message conforms to RFC 2045, see section 4 of that RFC)
+ QByteArray mimeHeader("MIME-Version");
+ if (!request.hasRawHeader(mimeHeader))
+ newRequest.setRawHeader(mimeHeader, QByteArray("1.0"));
+
+ QIODevice *device = multiPart->d_func()->device;
+ if (!device->isReadable()) {
+ if (!device->isOpen()) {
+ if (!device->open(QIODevice::ReadOnly))
+ qWarning("could not open device for reading");
+ } else {
+ qWarning("device is not readable");
+ }
+ }
+
+ return newRequest;
+}
+
QT_END_NAMESPACE
#include "moc_qnetworkaccessmanager.cpp"
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index c065791..91b313f 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -65,6 +65,7 @@ class QSslError;
#if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER)
class QNetworkConfiguration;
#endif
+class QHttpMultiPart;
class QNetworkReplyImplPrivate;
class QNetworkAccessManagerPrivate;
@@ -116,8 +117,10 @@ public:
QNetworkReply *get(const QNetworkRequest &request);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = 0);
@@ -156,6 +159,8 @@ protected:
private:
friend class QNetworkReplyImplPrivate;
+ friend class QNetworkAccessHttpBackend;
+
Q_DECLARE_PRIVATE(QNetworkAccessManager)
Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>))
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index ce9e0f1..a76eafd 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -59,6 +59,7 @@
#include "private/qobject_p.h"
#include "QtNetwork/qnetworkproxy.h"
#include "QtNetwork/qnetworksession.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
QT_BEGIN_NAMESPACE
@@ -72,16 +73,19 @@ class QNetworkAccessManagerPrivate: public QObjectPrivate
public:
QNetworkAccessManagerPrivate()
: networkCache(0), cookieJar(0),
+ httpThread(0),
#ifndef QT_NO_NETWORKPROXY
proxyFactory(0),
#endif
#ifndef QT_NO_BEARERMANAGEMENT
networkSession(0),
+ lastSessionState(QNetworkSession::Invalid),
networkAccessible(QNetworkAccessManager::Accessible),
online(false),
initializeSession(true),
#endif
- cookieJarCreated(false)
+ cookieJarCreated(false),
+ authenticationManager(new QNetworkAccessAuthenticationManager)
{ }
~QNetworkAccessManagerPrivate();
@@ -116,11 +120,15 @@ public:
void _q_networkSessionStateChanged(QNetworkSession::State state);
#endif
+ QNetworkRequest prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart);
+
// this is the cache for storing downloaded files
QAbstractNetworkCache *networkCache;
QNetworkCookieJar *cookieJar;
+ QThread *httpThread;
+
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy proxy;
@@ -129,6 +137,7 @@ public:
#ifndef QT_NO_BEARERMANAGEMENT
QSharedPointer<QNetworkSession> networkSession;
+ QNetworkSession::State lastSessionState;
QString networkConfiguration;
QNetworkAccessManager::NetworkAccessibility networkAccessible;
bool online;
@@ -137,6 +146,9 @@ public:
bool cookieJarCreated;
+ // The cache with authorization data:
+ QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
+
// 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.
QNetworkAccessCache objectCache;
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 983c6ee..0670738 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -395,8 +395,8 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
// qdtext = <any TEXT except <">>
// quoted-pair = "\" CHAR
- // If its NAME=VALUE, retain the value as is
- // refer to ttp://bugreports.qt.nokia.com/browse/QTBUG-17746
+ // If it is NAME=VALUE, retain the value as is
+ // refer to http://bugreports.qt.nokia.com/browse/QTBUG-17746
if (isNameValue)
second += '"';
++i;
@@ -432,7 +432,9 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
position = i;
for ( ; i < length; ++i) {
register char c = text.at(i);
- if (c == ',' || c == ';' || isLWS(c))
+ // for name value pairs, we want to parse until reaching the next ';'
+ // and not break when reaching a space char
+ if (c == ',' || c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
break;
}
@@ -487,7 +489,6 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
result += '=';
if ((d->value.contains(';') ||
d->value.contains(',') ||
- d->value.contains(' ') ||
d->value.contains('"')) &&
(!d->value.startsWith('"') &&
!d->value.endsWith('"'))) {
@@ -737,7 +738,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
// 4 digit Year
if (isNum
&& year == -1
- && dateString.length() >= at + 3) {
+ && dateString.length() > at + 3) {
if (isNumber(dateString[at + 1])
&& isNumber(dateString[at + 2])
&& isNumber(dateString[at + 3])) {
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
index e49a8e1..1a5f73b 100644
--- a/src/network/access/qnetworkcookiejar.cpp
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -40,12 +40,12 @@
****************************************************************************/
#include "qnetworkcookiejar.h"
-#include "qnetworkcookiejartlds_p.h"
#include "qnetworkcookiejar_p.h"
#include "QtNetwork/qnetworkcookie.h"
#include "QtCore/qurl.h"
#include "QtCore/qdatetime.h"
+#include "private/qtldurl_p.h"
QT_BEGIN_NAMESPACE
@@ -216,7 +216,7 @@ bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieLis
// the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
// redundant; the "leading dot" rule has been relaxed anyway, see above
// we remove the leading dot for this check
- if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
+ if (qIsEffectiveTLD(domain.remove(0, 1)))
continue; // not accepted
}
@@ -317,43 +317,4 @@ QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
return result;
}
-bool QNetworkCookieJarPrivate::isEffectiveTLD(const QString &domain)
-{
- // for domain 'foo.bar.com':
- // 1. return if TLD table contains 'foo.bar.com'
- if (containsTLDEntry(domain))
- return true;
-
- if (domain.contains(QLatin1Char('.'))) {
- int count = domain.size() - domain.indexOf(QLatin1Char('.'));
- QString wildCardDomain;
- wildCardDomain.reserve(count + 1);
- wildCardDomain.append(QLatin1Char('*'));
- wildCardDomain.append(domain.right(count));
- // 2. if table contains '*.bar.com',
- // test if table contains '!foo.bar.com'
- if (containsTLDEntry(wildCardDomain)) {
- QString exceptionDomain;
- exceptionDomain.reserve(domain.size() + 1);
- exceptionDomain.append(QLatin1Char('!'));
- exceptionDomain.append(domain);
- return (! containsTLDEntry(exceptionDomain));
- }
- }
- return false;
-}
-
-bool QNetworkCookieJarPrivate::containsTLDEntry(const QString &entry)
-{
- int index = qHash(entry) % tldCount;
- int currentDomainIndex = tldIndices[index];
- while (currentDomainIndex < tldIndices[index+1]) {
- QString currentEntry = QString::fromUtf8(tldData + currentDomainIndex);
- if (currentEntry == entry)
- return true;
- currentDomainIndex += qstrlen(tldData + currentDomainIndex) + 1; // +1 for the ending \0
- }
- return false;
-}
-
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
index 912847b..34858d9 100644
--- a/src/network/access/qnetworkcookiejar_p.h
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -63,9 +63,6 @@ class QNetworkCookieJarPrivate: public QObjectPrivate
public:
QList<QNetworkCookie> allCookies;
- static bool Q_AUTOTEST_EXPORT isEffectiveTLD(const QString &domain);
- static bool containsTLDEntry(const QString &entry);
-
Q_DECLARE_PUBLIC(QNetworkCookieJar)
};
diff --git a/src/network/access/qnetworkcookiejartlds_p.h b/src/network/access/qnetworkcookiejartlds_p.h
deleted file mode 100644
index b06d881..0000000
--- a/src/network/access/qnetworkcookiejartlds_p.h
+++ /dev/null
@@ -1,6481 +0,0 @@
-// Version: MPL 1.1/GPL 2.0/LGPL 2.1
-//
-// The contents of this file are subject to the Mozilla Public License Version
-// 1.1 (the "License"); you may not use this file except in compliance with
-// the License. You may obtain a copy of the License at
-// http://www.mozilla.org/MPL/
-//
-// Software distributed under the License is distributed on an "AS IS" basis,
-// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-// for the specific language governing rights and limitations under the
-// License.
-//
-// The Original Code is the Public Suffix List.
-//
-// The Initial Developer of the Original Code is
-// Jo Hermans <jo.hermans@gmail.com>.
-// Portions created by the Initial Developer are Copyright (C) 2007
-// the Initial Developer. All Rights Reserved.
-//
-// Contributor(s):
-// Ruben Arakelyan <ruben@wackomenace.co.uk>
-// Gervase Markham <gerv@gerv.net>
-// Pamela Greene <pamg.bugs@gmail.com>
-// David Triendl <david@triendl.name>
-// Jothan Frakes <jothan@gmail.com>
-// The kind representatives of many TLD registries
-//
-// Alternatively, the contents of this file may be used under the terms of
-// either the GNU General Public License Version 2 or later (the "GPL"), or
-// the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-// in which case the provisions of the GPL or the LGPL are applicable instead
-// of those above. If you wish to allow use of your version of this file only
-// under the terms of either the GPL or the LGPL, and not to allow others to
-// use your version of this file under the terms of the MPL, indicate your
-// decision by deleting the provisions above and replace them with the notice
-// and other provisions required by the GPL or the LGPL. If you do not delete
-// the provisions above, a recipient may use your version of this file under
-// the terms of any one of the MPL, the GPL or the LGPL.
-//
-
-#ifndef QNETWORKCOOKIEJARTLD_P_H
-#define QNETWORKCOOKIEJARTLD_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of the Network Access framework. This header file may change from
-// version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-QT_BEGIN_NAMESPACE
-
-// note to maintainer:
-// this file should be updated before each release ->
-// for instructions see the program at
-// util/network/cookiejar-generateTLDs
-
-static const quint16 tldCount = 3949;
-static const quint16 tldIndices[] = {
-0,
-7,
-14,
-14,
-20,
-51,
-61,
-93,
-100,
-100,
-116,
-159,
-167,
-180,
-180,
-193,
-234,
-234,
-234,
-255,
-255,
-255,
-280,
-280,
-287,
-287,
-295,
-303,
-313,
-326,
-326,
-380,
-393,
-413,
-419,
-419,
-419,
-424,
-438,
-438,
-469,
-515,
-515,
-515,
-534,
-534,
-557,
-557,
-557,
-557,
-572,
-572,
-572,
-579,
-587,
-597,
-612,
-612,
-624,
-636,
-648,
-662,
-687,
-709,
-714,
-740,
-766,
-789,
-789,
-805,
-805,
-810,
-815,
-815,
-824,
-824,
-831,
-857,
-869,
-891,
-891,
-916,
-916,
-916,
-927,
-934,
-964,
-971,
-987,
-987,
-987,
-1008,
-1008,
-1016,
-1016,
-1030,
-1030,
-1052,
-1075,
-1075,
-1082,
-1087,
-1115,
-1135,
-1135,
-1135,
-1172,
-1178,
-1178,
-1178,
-1202,
-1207,
-1220,
-1220,
-1266,
-1266,
-1266,
-1266,
-1272,
-1290,
-1316,
-1316,
-1332,
-1332,
-1339,
-1339,
-1352,
-1352,
-1389,
-1389,
-1408,
-1415,
-1437,
-1444,
-1489,
-1489,
-1502,
-1502,
-1512,
-1518,
-1539,
-1555,
-1562,
-1584,
-1598,
-1607,
-1607,
-1607,
-1632,
-1652,
-1652,
-1658,
-1658,
-1675,
-1682,
-1709,
-1733,
-1748,
-1776,
-1783,
-1783,
-1790,
-1797,
-1826,
-1850,
-1850,
-1856,
-1880,
-1887,
-1901,
-1921,
-1947,
-1961,
-1967,
-1967,
-1967,
-1972,
-1986,
-1986,
-1986,
-2009,
-2029,
-2029,
-2047,
-2061,
-2075,
-2075,
-2075,
-2075,
-2075,
-2075,
-2082,
-2082,
-2124,
-2124,
-2129,
-2162,
-2162,
-2162,
-2236,
-2256,
-2263,
-2276,
-2283,
-2313,
-2313,
-2347,
-2380,
-2387,
-2387,
-2387,
-2431,
-2438,
-2445,
-2452,
-2459,
-2459,
-2469,
-2490,
-2516,
-2527,
-2540,
-2540,
-2586,
-2610,
-2630,
-2630,
-2653,
-2660,
-2669,
-2693,
-2693,
-2710,
-2710,
-2719,
-2719,
-2734,
-2740,
-2740,
-2753,
-2753,
-2763,
-2770,
-2775,
-2782,
-2789,
-2802,
-2820,
-2827,
-2827,
-2841,
-2855,
-2855,
-2865,
-2872,
-2884,
-2884,
-2919,
-2937,
-2955,
-2962,
-3012,
-3042,
-3073,
-3083,
-3083,
-3100,
-3105,
-3112,
-3131,
-3131,
-3166,
-3180,
-3187,
-3194,
-3211,
-3218,
-3223,
-3233,
-3249,
-3259,
-3268,
-3314,
-3314,
-3324,
-3324,
-3336,
-3336,
-3336,
-3336,
-3350,
-3363,
-3376,
-3398,
-3416,
-3445,
-3464,
-3488,
-3488,
-3497,
-3545,
-3552,
-3552,
-3552,
-3566,
-3573,
-3573,
-3573,
-3581,
-3581,
-3603,
-3603,
-3615,
-3621,
-3621,
-3683,
-3683,
-3710,
-3710,
-3716,
-3716,
-3748,
-3770,
-3791,
-3803,
-3810,
-3817,
-3833,
-3846,
-3846,
-3852,
-3876,
-3876,
-3882,
-3903,
-3910,
-3939,
-3939,
-3939,
-3947,
-3962,
-3962,
-3981,
-3994,
-4021,
-4030,
-4042,
-4085,
-4085,
-4096,
-4096,
-4104,
-4123,
-4123,
-4123,
-4143,
-4154,
-4164,
-4194,
-4194,
-4194,
-4205,
-4205,
-4222,
-4238,
-4301,
-4309,
-4322,
-4331,
-4331,
-4331,
-4331,
-4331,
-4347,
-4365,
-4375,
-4375,
-4385,
-4398,
-4412,
-4430,
-4430,
-4437,
-4447,
-4463,
-4472,
-4472,
-4472,
-4484,
-4484,
-4484,
-4484,
-4484,
-4490,
-4490,
-4511,
-4511,
-4522,
-4522,
-4522,
-4522,
-4528,
-4528,
-4534,
-4551,
-4551,
-4551,
-4564,
-4583,
-4583,
-4611,
-4611,
-4611,
-4622,
-4622,
-4649,
-4668,
-4677,
-4692,
-4692,
-4692,
-4705,
-4723,
-4723,
-4723,
-4729,
-4729,
-4743,
-4743,
-4750,
-4750,
-4763,
-4770,
-4776,
-4776,
-4776,
-4793,
-4811,
-4811,
-4811,
-4821,
-4821,
-4841,
-4857,
-4891,
-4897,
-4903,
-4903,
-4903,
-4919,
-4935,
-4942,
-4958,
-4958,
-4975,
-4975,
-4975,
-4985,
-4985,
-5020,
-5032,
-5032,
-5040,
-5053,
-5068,
-5079,
-5079,
-5101,
-5115,
-5115,
-5135,
-5154,
-5161,
-5161,
-5168,
-5168,
-5184,
-5210,
-5210,
-5238,
-5255,
-5278,
-5285,
-5308,
-5318,
-5327,
-5327,
-5333,
-5333,
-5340,
-5348,
-5355,
-5366,
-5377,
-5384,
-5408,
-5415,
-5422,
-5435,
-5442,
-5482,
-5482,
-5482,
-5482,
-5498,
-5527,
-5534,
-5541,
-5572,
-5572,
-5572,
-5579,
-5591,
-5602,
-5602,
-5621,
-5621,
-5646,
-5664,
-5671,
-5671,
-5681,
-5696,
-5707,
-5716,
-5716,
-5723,
-5723,
-5732,
-5742,
-5742,
-5763,
-5770,
-5776,
-5790,
-5790,
-5797,
-5806,
-5816,
-5832,
-5882,
-5882,
-5882,
-5882,
-5893,
-5934,
-5934,
-5965,
-5965,
-5980,
-5980,
-5980,
-5980,
-6007,
-6017,
-6034,
-6051,
-6065,
-6075,
-6075,
-6091,
-6097,
-6097,
-6109,
-6109,
-6109,
-6122,
-6147,
-6168,
-6168,
-6191,
-6191,
-6191,
-6191,
-6191,
-6198,
-6217,
-6224,
-6224,
-6231,
-6245,
-6252,
-6252,
-6270,
-6270,
-6284,
-6305,
-6315,
-6322,
-6322,
-6322,
-6329,
-6329,
-6329,
-6353,
-6361,
-6361,
-6375,
-6391,
-6405,
-6405,
-6415,
-6431,
-6431,
-6431,
-6431,
-6458,
-6475,
-6475,
-6475,
-6482,
-6489,
-6496,
-6496,
-6503,
-6520,
-6520,
-6520,
-6527,
-6527,
-6544,
-6561,
-6573,
-6573,
-6580,
-6580,
-6587,
-6587,
-6592,
-6592,
-6592,
-6592,
-6599,
-6599,
-6612,
-6633,
-6649,
-6649,
-6669,
-6676,
-6683,
-6683,
-6713,
-6720,
-6727,
-6736,
-6736,
-6746,
-6770,
-6807,
-6814,
-6827,
-6846,
-6846,
-6864,
-6864,
-6864,
-6864,
-6881,
-6888,
-6888,
-6888,
-6914,
-6935,
-6935,
-6935,
-6953,
-6959,
-6966,
-6983,
-6983,
-6983,
-7013,
-7023,
-7023,
-7023,
-7023,
-7037,
-7044,
-7058,
-7079,
-7086,
-7123,
-7134,
-7155,
-7168,
-7178,
-7203,
-7227,
-7236,
-7258,
-7265,
-7274,
-7274,
-7303,
-7303,
-7314,
-7314,
-7345,
-7352,
-7367,
-7377,
-7388,
-7388,
-7402,
-7402,
-7409,
-7421,
-7421,
-7467,
-7484,
-7484,
-7484,
-7491,
-7532,
-7532,
-7539,
-7546,
-7546,
-7553,
-7573,
-7573,
-7573,
-7580,
-7587,
-7594,
-7594,
-7594,
-7606,
-7606,
-7637,
-7637,
-7637,
-7664,
-7664,
-7664,
-7677,
-7684,
-7701,
-7723,
-7723,
-7723,
-7723,
-7734,
-7734,
-7734,
-7748,
-7748,
-7748,
-7748,
-7759,
-7759,
-7775,
-7775,
-7782,
-7789,
-7817,
-7824,
-7831,
-7836,
-7865,
-7865,
-7876,
-7901,
-7901,
-7908,
-7918,
-7938,
-7945,
-7945,
-7957,
-7964,
-7979,
-7986,
-7994,
-8007,
-8007,
-8014,
-8021,
-8061,
-8061,
-8071,
-8088,
-8131,
-8138,
-8153,
-8160,
-8160,
-8160,
-8175,
-8183,
-8197,
-8211,
-8211,
-8211,
-8243,
-8243,
-8243,
-8243,
-8259,
-8259,
-8259,
-8259,
-8259,
-8266,
-8275,
-8281,
-8281,
-8281,
-8281,
-8288,
-8288,
-8309,
-8309,
-8309,
-8330,
-8330,
-8330,
-8330,
-8337,
-8343,
-8343,
-8360,
-8370,
-8370,
-8380,
-8380,
-8386,
-8386,
-8397,
-8415,
-8415,
-8428,
-8454,
-8460,
-8475,
-8492,
-8526,
-8554,
-8554,
-8583,
-8583,
-8583,
-8598,
-8607,
-8617,
-8617,
-8642,
-8652,
-8652,
-8652,
-8662,
-8688,
-8688,
-8704,
-8704,
-8747,
-8765,
-8775,
-8783,
-8811,
-8835,
-8835,
-8835,
-8850,
-8859,
-8884,
-8910,
-8919,
-8952,
-8978,
-8978,
-8991,
-8991,
-8991,
-8999,
-8999,
-8999,
-9030,
-9030,
-9030,
-9030,
-9030,
-9041,
-9048,
-9048,
-9054,
-9054,
-9054,
-9086,
-9108,
-9108,
-9119,
-9119,
-9130,
-9144,
-9152,
-9161,
-9174,
-9194,
-9207,
-9207,
-9207,
-9232,
-9242,
-9242,
-9271,
-9290,
-9308,
-9308,
-9308,
-9308,
-9320,
-9333,
-9343,
-9356,
-9379,
-9379,
-9379,
-9395,
-9395,
-9406,
-9419,
-9419,
-9419,
-9419,
-9450,
-9485,
-9485,
-9497,
-9497,
-9505,
-9517,
-9528,
-9528,
-9551,
-9564,
-9586,
-9586,
-9608,
-9608,
-9626,
-9626,
-9626,
-9653,
-9653,
-9681,
-9681,
-9681,
-9698,
-9698,
-9698,
-9714,
-9729,
-9737,
-9737,
-9765,
-9765,
-9765,
-9765,
-9765,
-9825,
-9844,
-9866,
-9880,
-9880,
-9880,
-9880,
-9886,
-9895,
-9895,
-9895,
-9895,
-9895,
-9913,
-9913,
-9924,
-9958,
-9958,
-9967,
-9975,
-9975,
-9975,
-9981,
-9998,
-9998,
-9998,
-10012,
-10036,
-10036,
-10066,
-10079,
-10097,
-10121,
-10133,
-10142,
-10142,
-10142,
-10156,
-10173,
-10173,
-10173,
-10196,
-10205,
-10205,
-10205,
-10205,
-10218,
-10234,
-10240,
-10240,
-10240,
-10264,
-10273,
-10286,
-10286,
-10286,
-10286,
-10299,
-10299,
-10309,
-10309,
-10339,
-10358,
-10358,
-10374,
-10374,
-10390,
-10390,
-10413,
-10413,
-10439,
-10461,
-10467,
-10467,
-10467,
-10492,
-10492,
-10501,
-10528,
-10528,
-10528,
-10539,
-10583,
-10583,
-10583,
-10613,
-10613,
-10619,
-10628,
-10645,
-10645,
-10645,
-10650,
-10671,
-10687,
-10709,
-10709,
-10709,
-10709,
-10709,
-10727,
-10727,
-10727,
-10727,
-10733,
-10733,
-10768,
-10768,
-10773,
-10780,
-10788,
-10788,
-10797,
-10797,
-10835,
-10835,
-10845,
-10852,
-10861,
-10861,
-10861,
-10861,
-10861,
-10861,
-10861,
-10875,
-10888,
-10907,
-10907,
-10907,
-10920,
-10920,
-10932,
-10946,
-10977,
-10977,
-10997,
-11008,
-11037,
-11059,
-11059,
-11059,
-11067,
-11067,
-11067,
-11067,
-11067,
-11077,
-11091,
-11102,
-11102,
-11112,
-11125,
-11129,
-11129,
-11154,
-11154,
-11154,
-11164,
-11164,
-11189,
-11189,
-11189,
-11189,
-11189,
-11196,
-11196,
-11227,
-11238,
-11247,
-11256,
-11265,
-11265,
-11286,
-11307,
-11330,
-11337,
-11378,
-11378,
-11389,
-11413,
-11454,
-11469,
-11475,
-11475,
-11475,
-11475,
-11503,
-11503,
-11503,
-11503,
-11534,
-11534,
-11550,
-11550,
-11557,
-11557,
-11557,
-11557,
-11574,
-11585,
-11585,
-11603,
-11626,
-11643,
-11643,
-11643,
-11643,
-11663,
-11663,
-11682,
-11696,
-11696,
-11696,
-11696,
-11706,
-11706,
-11706,
-11706,
-11723,
-11723,
-11757,
-11757,
-11773,
-11795,
-11813,
-11836,
-11836,
-11883,
-11903,
-11952,
-11965,
-11965,
-11984,
-11997,
-12008,
-12008,
-12008,
-12024,
-12043,
-12065,
-12071,
-12099,
-12129,
-12140,
-12140,
-12146,
-12161,
-12161,
-12161,
-12161,
-12167,
-12167,
-12180,
-12186,
-12208,
-12226,
-12243,
-12243,
-12252,
-12252,
-12274,
-12289,
-12302,
-12302,
-12313,
-12313,
-12313,
-12313,
-12358,
-12358,
-12358,
-12369,
-12375,
-12391,
-12391,
-12391,
-12391,
-12405,
-12410,
-12416,
-12416,
-12436,
-12436,
-12436,
-12443,
-12443,
-12462,
-12477,
-12492,
-12492,
-12503,
-12519,
-12525,
-12531,
-12571,
-12571,
-12591,
-12591,
-12601,
-12641,
-12641,
-12641,
-12657,
-12657,
-12657,
-12699,
-12699,
-12699,
-12712,
-12728,
-12744,
-12761,
-12769,
-12782,
-12793,
-12823,
-12836,
-12851,
-12863,
-12890,
-12900,
-12900,
-12900,
-12910,
-12910,
-12924,
-12924,
-12924,
-12924,
-12924,
-12952,
-12984,
-13003,
-13038,
-13038,
-13056,
-13056,
-13056,
-13056,
-13074,
-13081,
-13093,
-13103,
-13103,
-13112,
-13119,
-13132,
-13132,
-13132,
-13141,
-13151,
-13183,
-13193,
-13206,
-13206,
-13206,
-13236,
-13252,
-13267,
-13267,
-13294,
-13307,
-13307,
-13307,
-13343,
-13349,
-13349,
-13349,
-13375,
-13375,
-13375,
-13384,
-13384,
-13384,
-13393,
-13393,
-13393,
-13402,
-13414,
-13425,
-13445,
-13467,
-13485,
-13499,
-13509,
-13528,
-13528,
-13549,
-13549,
-13559,
-13570,
-13570,
-13570,
-13570,
-13598,
-13637,
-13647,
-13647,
-13661,
-13673,
-13682,
-13687,
-13694,
-13694,
-13720,
-13733,
-13742,
-13748,
-13771,
-13795,
-13795,
-13814,
-13821,
-13821,
-13855,
-13862,
-13862,
-13862,
-13874,
-13874,
-13897,
-13909,
-13909,
-13947,
-13947,
-13952,
-13952,
-13970,
-13979,
-13979,
-13979,
-14008,
-14049,
-14049,
-14049,
-14049,
-14049,
-14049,
-14060,
-14083,
-14083,
-14091,
-14101,
-14101,
-14101,
-14101,
-14118,
-14136,
-14195,
-14195,
-14195,
-14213,
-14213,
-14232,
-14232,
-14253,
-14253,
-14258,
-14275,
-14275,
-14304,
-14311,
-14311,
-14318,
-14318,
-14318,
-14318,
-14318,
-14318,
-14325,
-14325,
-14345,
-14345,
-14379,
-14389,
-14422,
-14422,
-14422,
-14422,
-14435,
-14441,
-14441,
-14460,
-14460,
-14471,
-14471,
-14481,
-14495,
-14495,
-14495,
-14502,
-14502,
-14502,
-14502,
-14524,
-14533,
-14541,
-14552,
-14552,
-14552,
-14552,
-14563,
-14563,
-14568,
-14568,
-14585,
-14595,
-14602,
-14628,
-14628,
-14645,
-14672,
-14678,
-14697,
-14697,
-14734,
-14757,
-14764,
-14771,
-14771,
-14771,
-14796,
-14815,
-14822,
-14845,
-14861,
-14861,
-14861,
-14873,
-14873,
-14873,
-14902,
-14920,
-14920,
-14926,
-14926,
-14926,
-14941,
-14949,
-14949,
-14949,
-14949,
-14949,
-14960,
-14960,
-14971,
-14971,
-14998,
-15003,
-15003,
-15003,
-15013,
-15013,
-15027,
-15027,
-15027,
-15043,
-15053,
-15063,
-15074,
-15083,
-15093,
-15093,
-15121,
-15121,
-15128,
-15128,
-15144,
-15144,
-15144,
-15144,
-15165,
-15170,
-15170,
-15170,
-15170,
-15170,
-15170,
-15170,
-15186,
-15206,
-15206,
-15206,
-15224,
-15236,
-15236,
-15252,
-15258,
-15258,
-15258,
-15258,
-15264,
-15277,
-15288,
-15307,
-15307,
-15318,
-15328,
-15328,
-15334,
-15334,
-15363,
-15399,
-15399,
-15422,
-15438,
-15447,
-15447,
-15456,
-15456,
-15456,
-15495,
-15495,
-15495,
-15495,
-15511,
-15511,
-15530,
-15557,
-15566,
-15582,
-15590,
-15590,
-15604,
-15604,
-15625,
-15635,
-15655,
-15655,
-15655,
-15655,
-15655,
-15665,
-15675,
-15675,
-15675,
-15675,
-15675,
-15682,
-15682,
-15682,
-15694,
-15719,
-15749,
-15749,
-15794,
-15794,
-15837,
-15854,
-15854,
-15861,
-15861,
-15861,
-15861,
-15884,
-15900,
-15911,
-15931,
-15931,
-15931,
-15931,
-15931,
-15963,
-15963,
-15996,
-16006,
-16013,
-16024,
-16034,
-16049,
-16049,
-16049,
-16059,
-16059,
-16059,
-16059,
-16059,
-16059,
-16059,
-16064,
-16075,
-16104,
-16104,
-16117,
-16124,
-16124,
-16124,
-16124,
-16130,
-16145,
-16159,
-16190,
-16193,
-16196,
-16210,
-16224,
-16243,
-16255,
-16261,
-16280,
-16283,
-16292,
-16295,
-16295,
-16301,
-16304,
-16322,
-16325,
-16328,
-16349,
-16355,
-16382,
-16408,
-16414,
-16414,
-16414,
-16458,
-16477,
-16480,
-16485,
-16488,
-16514,
-16520,
-16530,
-16530,
-16546,
-16562,
-16597,
-16603,
-16622,
-16622,
-16646,
-16669,
-16672,
-16715,
-16715,
-16715,
-16718,
-16718,
-16727,
-16733,
-16752,
-16770,
-16787,
-16794,
-16810,
-16827,
-16840,
-16850,
-16876,
-16901,
-16901,
-16901,
-16916,
-16919,
-16926,
-16943,
-16972,
-16978,
-16978,
-16978,
-16981,
-17008,
-17008,
-17016,
-17053,
-17053,
-17053,
-17053,
-17072,
-17086,
-17105,
-17139,
-17153,
-17162,
-17162,
-17177,
-17177,
-17177,
-17177,
-17195,
-17218,
-17221,
-17221,
-17237,
-17276,
-17276,
-17300,
-17319,
-17339,
-17357,
-17370,
-17383,
-17400,
-17407,
-17419,
-17439,
-17439,
-17449,
-17465,
-17475,
-17504,
-17527,
-17527,
-17534,
-17548,
-17564,
-17564,
-17598,
-17598,
-17601,
-17630,
-17649,
-17669,
-17669,
-17679,
-17686,
-17757,
-17776,
-17796,
-17810,
-17840,
-17840,
-17877,
-17884,
-17884,
-17911,
-17936,
-17970,
-17980,
-17995,
-17995,
-18008,
-18011,
-18036,
-18075,
-18097,
-18097,
-18104,
-18115,
-18115,
-18129,
-18134,
-18141,
-18141,
-18157,
-18180,
-18190,
-18217,
-18224,
-18252,
-18284,
-18284,
-18296,
-18299,
-18312,
-18322,
-18338,
-18348,
-18348,
-18363,
-18372,
-18387,
-18387,
-18390,
-18402,
-18445,
-18445,
-18445,
-18466,
-18479,
-18479,
-18498,
-18508,
-18511,
-18511,
-18511,
-18511,
-18511,
-18526,
-18558,
-18586,
-18622,
-18643,
-18670,
-18686,
-18710,
-18720,
-18739,
-18739,
-18742,
-18745,
-18771,
-18774,
-18777,
-18791,
-18813,
-18816,
-18822,
-18839,
-18845,
-18851,
-18854,
-18878,
-18881,
-18896,
-18896,
-18899,
-18926,
-18937,
-18940,
-18953,
-18963,
-19010,
-19010,
-19017,
-19046,
-19060,
-19060,
-19087,
-19095,
-19101,
-19101,
-19128,
-19146,
-19157,
-19157,
-19188,
-19198,
-19205,
-19223,
-19230,
-19255,
-19280,
-19280,
-19283,
-19292,
-19301,
-19320,
-19323,
-19323,
-19341,
-19341,
-19365,
-19378,
-19381,
-19394,
-19423,
-19433,
-19440,
-19466,
-19490,
-19490,
-19490,
-19497,
-19504,
-19511,
-19511,
-19518,
-19545,
-19568,
-19575,
-19575,
-19583,
-19586,
-19592,
-19592,
-19609,
-19622,
-19629,
-19641,
-19641,
-19656,
-19673,
-19700,
-19723,
-19733,
-19746,
-19759,
-19769,
-19782,
-19789,
-19795,
-19831,
-19834,
-19841,
-19851,
-19854,
-19880,
-19895,
-19898,
-19898,
-19911,
-19922,
-19950,
-20020,
-20030,
-20059,
-20062,
-20089,
-20107,
-20151,
-20154,
-20175,
-20205,
-20208,
-20229,
-20229,
-20255,
-20261,
-20261,
-20283,
-20335,
-20362,
-20385,
-20392,
-20392,
-20392,
-20392,
-20418,
-20418,
-20418,
-20418,
-20443,
-20446,
-20446,
-20452,
-20452,
-20467,
-20467,
-20489,
-20489,
-20498,
-20525,
-20554,
-20560,
-20575,
-20589,
-20589,
-20589,
-20614,
-20652,
-20659,
-20659,
-20668,
-20679,
-20679,
-20679,
-20685,
-20685,
-20685,
-20685,
-20685,
-20685,
-20696,
-20733,
-20760,
-20766,
-20769,
-20792,
-20792,
-20792,
-20792,
-20813,
-20813,
-20813,
-20813,
-20826,
-20826,
-20846,
-20862,
-20880,
-20887,
-20901,
-20908,
-20933,
-20938,
-20958,
-20969,
-20978,
-20978,
-20978,
-20985,
-20985,
-20985,
-21000,
-21010,
-21010,
-21014,
-21026,
-21033,
-21041,
-21041,
-21051,
-21051,
-21064,
-21071,
-21113,
-21120,
-21120,
-21127,
-21134,
-21142,
-21164,
-21164,
-21164,
-21188,
-21195,
-21208,
-21215,
-21226,
-21226,
-21226,
-21238,
-21249,
-21255,
-21255,
-21265,
-21279,
-21296,
-21301,
-21315,
-21321,
-21331,
-21331,
-21331,
-21337,
-21343,
-21343,
-21351,
-21351,
-21361,
-21368,
-21383,
-21383,
-21389,
-21413,
-21437,
-21449,
-21462,
-21478,
-21506,
-21534,
-21546,
-21546,
-21557,
-21580,
-21595,
-21595,
-21595,
-21595,
-21626,
-21626,
-21646,
-21670,
-21676,
-21688,
-21688,
-21716,
-21731,
-21762,
-21762,
-21762,
-21762,
-21762,
-21783,
-21789,
-21799,
-21828,
-21828,
-21837,
-21845,
-21868,
-21874,
-21874,
-21880,
-21880,
-21889,
-21901,
-21910,
-21941,
-21941,
-21959,
-21959,
-21959,
-21966,
-21966,
-21972,
-21972,
-21995,
-22011,
-22043,
-22043,
-22051,
-22051,
-22060,
-22060,
-22060,
-22074,
-22086,
-22086,
-22099,
-22099,
-22120,
-22120,
-22134,
-22144,
-22150,
-22158,
-22164,
-22164,
-22171,
-22199,
-22210,
-22210,
-22210,
-22220,
-22228,
-22228,
-22239,
-22261,
-22304,
-22304,
-22312,
-22349,
-22349,
-22349,
-22357,
-22381,
-22381,
-22390,
-22390,
-22390,
-22390,
-22402,
-22413,
-22413,
-22422,
-22422,
-22445,
-22445,
-22445,
-22456,
-22456,
-22469,
-22479,
-22501,
-22512,
-22528,
-22528,
-22528,
-22528,
-22528,
-22528,
-22528,
-22540,
-22540,
-22546,
-22546,
-22546,
-22546,
-22569,
-22591,
-22591,
-22591,
-22591,
-22610,
-22655,
-22655,
-22667,
-22667,
-22677,
-22677,
-22692,
-22692,
-22702,
-22702,
-22702,
-22717,
-22736,
-22750,
-22755,
-22780,
-22785,
-22822,
-22844,
-22859,
-22871,
-22909,
-22949,
-22962,
-22973,
-22979,
-22988,
-23007,
-23027,
-23035,
-23072,
-23082,
-23082,
-23109,
-23116,
-23130,
-23158,
-23166,
-23166,
-23172,
-23177,
-23189,
-23219,
-23219,
-23250,
-23273,
-23281,
-23281,
-23281,
-23281,
-23281,
-23287,
-23287,
-23299,
-23305,
-23315,
-23315,
-23321,
-23328,
-23334,
-23355,
-23355,
-23355,
-23355,
-23355,
-23366,
-23390,
-23396,
-23396,
-23396,
-23396,
-23402,
-23418,
-23424,
-23424,
-23438,
-23446,
-23446,
-23446,
-23500,
-23525,
-23569,
-23592,
-23592,
-23592,
-23605,
-23614,
-23614,
-23614,
-23627,
-23633,
-23657,
-23673,
-23673,
-23673,
-23689,
-23689,
-23701,
-23701,
-23701,
-23713,
-23713,
-23713,
-23738,
-23758,
-23775,
-23775,
-23794,
-23794,
-23803,
-23803,
-23803,
-23803,
-23813,
-23826,
-23845,
-23845,
-23845,
-23845,
-23872,
-23872,
-23872,
-23888,
-23888,
-23900,
-23900,
-23906,
-23906,
-23906,
-23906,
-23906,
-23924,
-23924,
-23924,
-23930,
-23930,
-23939,
-23949,
-23971,
-23971,
-23971,
-24000,
-24012,
-24042,
-24042,
-24042,
-24042,
-24042,
-24070,
-24076,
-24094,
-24094,
-24094,
-24123,
-24123,
-24123,
-24134,
-24150,
-24150,
-24150,
-24150,
-24150,
-24150,
-24155,
-24179,
-24179,
-24189,
-24189,
-24189,
-24198,
-24198,
-24218,
-24218,
-24218,
-24234,
-24251,
-24257,
-24276,
-24305,
-24321,
-24321,
-24321,
-24334,
-24334,
-24334,
-24349,
-24356,
-24361,
-24372,
-24372,
-24372,
-24388,
-24396,
-24396,
-24402,
-24410,
-24410,
-24428,
-24428,
-24450,
-24450,
-24467,
-24485,
-24495,
-24495,
-24495,
-24507,
-24507,
-24514,
-24531,
-24531,
-24531,
-24531,
-24531,
-24537,
-24537,
-24558,
-24572,
-24584,
-24584,
-24601,
-24601,
-24607,
-24615,
-24615,
-24632,
-24632,
-24632,
-24632,
-24661,
-24676,
-24676,
-24724,
-24751,
-24751,
-24774,
-24774,
-24783,
-24793,
-24793,
-24793,
-24813,
-24819,
-24819,
-24826,
-24826,
-24842,
-24858,
-24872,
-24872,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24890,
-24906,
-24906,
-24917,
-24928,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24947,
-24963,
-24983,
-24991,
-24991,
-24991,
-24991,
-24991,
-24991,
-24991,
-24991,
-25007,
-25007,
-25007,
-25007,
-25007,
-25007,
-25007,
-25030,
-25040,
-25040,
-25040,
-25040,
-25040,
-25059,
-25097,
-25132,
-25149,
-25159,
-25169,
-25169,
-25169,
-25192,
-25192,
-25192,
-25192,
-25205,
-25205,
-25216,
-25221,
-25221,
-25233,
-25233,
-25240,
-25250,
-25256,
-25273,
-25273,
-25303,
-25321,
-25321,
-25321,
-25333,
-25333,
-25333,
-25333,
-25370,
-25370,
-25402,
-25418,
-25418,
-25439,
-25439,
-25454,
-25454,
-25454,
-25463,
-25477,
-25526,
-25526,
-25526,
-25526,
-25545,
-25562,
-25572,
-25572,
-25582,
-25582,
-25582,
-25597,
-25610,
-25634,
-25641,
-25641,
-25641,
-25668,
-25668,
-25675,
-25707,
-25727,
-25727,
-25741,
-25756,
-25756,
-25779,
-25811,
-25811,
-25811,
-25817,
-25817,
-25817,
-25827,
-25827,
-25827,
-25827,
-25827,
-25836,
-25836,
-25836,
-25836,
-25850,
-25850,
-25860,
-25884,
-25901,
-25922,
-25936,
-25946,
-25969,
-25969,
-25969,
-25969,
-25975,
-25975,
-25987,
-25987,
-26065,
-26065,
-26065,
-26084,
-26084,
-26103,
-26128,
-26141,
-26151,
-26169,
-26169,
-26169,
-26180,
-26191,
-26191,
-26191,
-26197,
-26197,
-26205,
-26211,
-26235,
-26235,
-26235,
-26235,
-26235,
-26235,
-26245,
-26245,
-26259,
-26259,
-26259,
-26259,
-26284,
-26284,
-26325,
-26325,
-26355,
-26364,
-26364,
-26402,
-26418,
-26418,
-26425,
-26432,
-26432,
-26454,
-26504,
-26513,
-26525,
-26525,
-26525,
-26525,
-26525,
-26545,
-26545,
-26571,
-26590,
-26597,
-26597,
-26597,
-26597,
-26597,
-26639,
-26648,
-26659,
-26666,
-26672,
-26672,
-26672,
-26672,
-26672,
-26694,
-26701,
-26701,
-26701,
-26724,
-26724,
-26746,
-26753,
-26774,
-26774,
-26774,
-26774,
-26806,
-26824,
-26824,
-26830,
-26852,
-26882,
-26882,
-26889,
-26889,
-26889,
-26889,
-26889,
-26889,
-26903,
-26911,
-26918,
-26918,
-26928,
-26948,
-26948,
-26970,
-26970,
-26985,
-26996,
-27045,
-27045,
-27058,
-27058,
-27068,
-27068,
-27104,
-27155,
-27155,
-27155,
-27155,
-27155,
-27172,
-27172,
-27172,
-27172,
-27189,
-27195,
-27195,
-27223,
-27223,
-27223,
-27223,
-27244,
-27290,
-27322,
-27332,
-27364,
-27371,
-27371,
-27371,
-27401,
-27401,
-27417,
-27417,
-27431,
-27470,
-27470,
-27470,
-27470,
-27484,
-27484,
-27484,
-27484,
-27484,
-27496,
-27496,
-27515,
-27515,
-27543,
-27560,
-27576,
-27604,
-27622,
-27631,
-27631,
-27638,
-27649,
-27672,
-27682,
-27682,
-27682,
-27707,
-27717,
-27724,
-27732,
-27755,
-27755,
-27755,
-27768,
-27768,
-27783,
-27789,
-27799,
-27799,
-27799,
-27818,
-27826,
-27838,
-27848,
-27848,
-27848,
-27855,
-27862,
-27862,
-27896,
-27921,
-27921,
-27943,
-27954,
-27954,
-27976,
-27976,
-27976,
-27985,
-28002,
-28012,
-28019,
-28034,
-28034,
-28046,
-28068,
-28097,
-28122,
-28122,
-28131,
-28137,
-28151,
-28151,
-28172,
-28190,
-28196,
-28211,
-28211,
-28264,
-28273,
-28286,
-28324,
-28324,
-28324,
-28354,
-28354,
-28361,
-28397,
-28417,
-28417,
-28424,
-28435,
-28461,
-28461,
-28470,
-28483,
-28483,
-28483,
-28483,
-28483,
-28483,
-28483,
-28515,
-28531,
-28531,
-28549,
-28575,
-28575,
-28575,
-28582,
-28599,
-28615,
-28630,
-28630,
-28672,
-28711,
-28723,
-28723,
-28731,
-28752,
-28752,
-28752,
-28763,
-28763,
-28775,
-28775,
-28775,
-28775,
-28775,
-28775,
-28805,
-28814,
-28830,
-28861,
-28882,
-28882,
-28902,
-28918,
-28937,
-28952,
-28959,
-28998,
-29009,
-29009,
-29009,
-29009,
-29019,
-29019,
-29019,
-29019,
-29019,
-29057,
-29069,
-29076,
-29076,
-29076,
-29076,
-29076,
-29082,
-29082,
-29082,
-29117,
-29117,
-29117,
-29117,
-29134,
-29134,
-29159,
-29159,
-29185,
-29185,
-29196,
-29196,
-29242,
-29248,
-29256,
-29280,
-29301,
-29307,
-29307,
-29307,
-29314,
-29314,
-29338,
-29356,
-29367,
-29367,
-29381,
-29391,
-29399,
-29399,
-29414,
-29434,
-29434,
-29441,
-29473,
-29484,
-29503,
-29520,
-29520,
-29548,
-29565,
-29572,
-29572,
-29572,
-29572,
-29572,
-29597,
-29597,
-29620,
-29655,
-29660,
-29660,
-29660,
-29667,
-29674,
-29688,
-29698,
-29705,
-29728,
-29740,
-29740,
-29761,
-29761,
-29767,
-29780,
-29787,
-29794,
-29794,
-29807,
-29820,
-29820,
-29820,
-29820,
-29832,
-29844,
-29855,
-29855,
-29855,
-29867,
-29867,
-29867,
-29867,
-29881,
-29881,
-29905,
-29905,
-29905,
-29923,
-29923,
-29948,
-29948,
-29948,
-29976,
-29986,
-29996,
-29996,
-30030,
-30030,
-30054,
-30054,
-30070,
-30070,
-30070,
-30077,
-30077,
-30087,
-30107,
-30107,
-30115,
-30141,
-30178,
-30178,
-30201,
-30201,
-30201,
-30207,
-30229,
-30239,
-30254,
-30268,
-30277,
-30311,
-30323,
-30323,
-30331,
-30331,
-30331,
-30331,
-30331,
-30353,
-30365,
-30374,
-30374,
-30374,
-30380,
-30380,
-30380,
-30380,
-30410,
-30410,
-30410,
-30443,
-30443,
-30453,
-30462,
-30472,
-30472,
-30472,
-30472,
-30472,
-30472,
-30489,
-30500,
-30500,
-30500,
-30532,
-30532,
-30553,
-30577,
-30599,
-30599,
-30609,
-30640,
-30640,
-30640,
-30664,
-30676,
-30676,
-30676,
-30692,
-30719,
-30728,
-30728,
-30742,
-30742,
-30749,
-30749,
-30760,
-30770,
-30770,
-30783,
-30783,
-30783,
-30804,
-30847,
-30847,
-30847,
-30847,
-30857,
-30857,
-30857,
-30857,
-30875,
-30875,
-30895,
-30895,
-30921,
-30926,
-30926,
-30926,
-30945,
-30945,
-30945,
-30945,
-30959,
-30959,
-30959,
-30959,
-30972,
-30972,
-30984,
-31011,
-31011,
-31048,
-31056,
-31056,
-31070,
-31077,
-31077,
-31110,
-31110,
-31115,
-31122,
-31139,
-31159,
-31159,
-31165,
-31171,
-31171,
-31197,
-31204,
-31211,
-31211,
-31221,
-31228,
-31228,
-31245,
-31245,
-31245,
-31252,
-31265,
-31265,
-31265,
-31265,
-31294,
-31305,
-31320,
-31333,
-31333,
-31333,
-31343,
-31350,
-31357,
-31369,
-31369,
-31379,
-31385,
-31391,
-31407,
-31407,
-31407,
-31423,
-31423,
-31423,
-31434,
-31454,
-31470,
-31511,
-31521,
-31521,
-31521,
-31542,
-31582,
-31582,
-31597,
-31597,
-31597,
-31614,
-31623,
-31645,
-31645,
-31661,
-31661,
-31669,
-31669,
-31676,
-31676,
-31706,
-31720,
-31726,
-31743,
-31785,
-31804,
-31817,
-31817,
-31835,
-31846,
-31863,
-31885,
-31885,
-31896,
-31907,
-31907,
-31907,
-31922,
-31922,
-31929,
-31929,
-31929,
-31936,
-31943,
-31949,
-31949,
-31949,
-31959,
-32006,
-32024,
-32031,
-32031,
-32038,
-32063,
-32095,
-32095,
-32105,
-32105,
-32105,
-32105,
-32105,
-32125,
-32134,
-32140,
-32176,
-32185,
-32195,
-32195,
-32195,
-32195,
-32202,
-32202,
-32218,
-32236,
-32259,
-32294,
-32300,
-32305,
-32305,
-32305,
-32323,
-32337,
-32352,
-32359,
-32374,
-32381,
-32388,
-32388,
-32388,
-32402,
-32402,
-32402,
-32402,
-32418,
-32428,
-32428,
-32428,
-32450,
-32450,
-32450,
-32462,
-32467,
-32480,
-32480,
-32480,
-32487,
-32502,
-32509,
-32525,
-32560,
-32570,
-32583,
-32597,
-32623,
-32637,
-32644,
-32667,
-32707,
-32725,
-32725,
-32747,
-32747,
-32751,
-32758,
-32789,
-32807,
-32824,
-32824,
-32824,
-32824,
-32843,
-32843,
-32850,
-32876,
-32908,
-32915,
-32946,
-32965,
-32965,
-32982,
-33002,
-33009,
-33029,
-33064,
-33084,
-33098,
-33098,
-33098,
-33098,
-33110,
-33110,
-33151,
-33158,
-33180,
-33198,
-33205,
-33227,
-33227,
-33237,
-33237,
-33253,
-33258,
-33277,
-33292,
-33315,
-33315,
-33333,
-33348,
-33348,
-33348,
-33348,
-33348,
-33348,
-33348,
-33355,
-33355,
-33355,
-33390,
-33408,
-33423,
-33437,
-33452,
-33458,
-33465,
-33480,
-33480,
-33487,
-33494,
-33504,
-33511,
-33551,
-33551,
-33558,
-33589,
-33595,
-33595,
-33602,
-33627,
-33644,
-33668,
-33668,
-33668,
-33676,
-33676,
-33716,
-33728,
-33747,
-33747,
-33769,
-33775,
-33789,
-33803,
-33803,
-33810,
-33810,
-33810,
-33820,
-33820,
-33820,
-33820,
-33843,
-33843,
-33843,
-33879,
-33889,
-33889,
-33889,
-33903,
-33917,
-33931,
-33959,
-33993,
-34000,
-34014,
-34037,
-34043,
-34055,
-34055,
-34077,
-34083,
-34090,
-34099,
-34099,
-34115,
-34115,
-34133,
-34140,
-34167,
-34172,
-34184,
-34221,
-34245,
-34252,
-34252,
-34259,
-34318,
-34318,
-34325,
-34325,
-34352,
-34400,
-34415,
-34422,
-34422,
-34431,
-34438,
-34445,
-34445,
-34473,
-34473,
-34489,
-34489,
-34489,
-34489,
-34499,
-34499,
-34499,
-34516,
-34536,
-34551,
-34564,
-34580,
-34580,
-34580,
-34589,
-34589,
-34589,
-34613,
-34648,
-34648,
-34648,
-34655,
-34664,
-34681,
-34681,
-34698,
-34698,
-34720,
-34736,
-34749,
-34749,
-34765,
-34778,
-34785,
-34795,
-34819,
-34819,
-34829,
-34841,
-34848,
-34854,
-34854,
-34854,
-34878,
-34894,
-34894,
-34900,
-34917,
-34934,
-34940,
-34970,
-34998,
-34998,
-35004,
-35004,
-35012,
-35012,
-35012,
-35020,
-35020,
-35032,
-35038,
-35062,
-35062,
-35062,
-35068,
-35068,
-35082,
-35092,
-35096,
-35107,
-35118,
-35134,
-35155,
-35155,
-35166,
-35178,
-35178,
-35195,
-35201,
-35201,
-35201,
-35226,
-35226,
-35226,
-35226,
-35256,
-35262,
-35272,
-35280,
-35299,
-35332,
-35354,
-35354,
-35354,
-35370,
-35386,
-35417,
-35417,
-35460,
-35473,
-35478,
-35495,
-35504,
-35504,
-35518,
-35552,
-35589,
-35624,
-35624,
-35637,
-35637,
-35643,
-35643,
-35669,
-35682,
-35695,
-35702,
-35709,
-35709,
-35726,
-35739,
-35749,
-35756,
-35756,
-35778,
-35803,
-35810,
-35829,
-35883,
-35899,
-35905,
-35911,
-35911,
-35923,
-35947,
-35954,
-35980,
-35987,
-36034,
-36052,
-36063,
-36095,
-36106,
-36106,
-36113,
-36120,
-36140,
-36140,
-36153,
-36160,
-36160,
-36167,
-36203,
-36203,
-36218,
-36218,
-36225,
-36253,
-36259,
-36284,
-36296,
-36310,
-36324,
-36331,
-36344,
-36367,
-36367,
-36367,
-36412,
-36412,
-36422,
-36463,
-36463,
-36463,
-36479,
-36490,
-36513,
-36520,
-36520,
-36527,
-36527,
-36527,
-36540,
-36574,
-36594,
-36594,
-36605,
-36621,
-36621,
-36641,
-36641,
-36641,
-36659,
-36682,
-36682,
-36682,
-36682,
-36705,
-36705,
-36705,
-36720,
-36720,
-36755,
-36755,
-36771,
-36771,
-36771,
-36788,
-36806,
-36835,
-36845,
-36875,
-36875,
-36903,
-36921,
-36928,
-36928,
-36940,
-36940,
-36940,
-36966,
-36966,
-36973,
-36983,
-36998,
-37004,
-37014,
-37024,
-37024,
-37032,
-37038,
-37038,
-37061,
-37074,
-37074,
-37091,
-37098,
-37105,
-37105,
-37133,
-37141,
-37141,
-37148,
-37191,
-37191,
-37197,
-37197,
-37210,
-37224,
-37224,
-37231,
-37250,
-37257,
-37273,
-37273,
-37280,
-37287,
-37294,
-37300,
-37307,
-37330,
-37348,
-37348,
-37359,
-37359,
-37359,
-37377,
-37392,
-37398,
-37412,
-37431,
-37469,
-37486,
-37508,
-37517,
-37535,
-37535,
-37542,
-37542,
-37549,
-37549,
-37549,
-37549,
-37556,
-37576,
-37576,
-37583,
-37590,
-37597,
-37604,
-37604,
-37621,
-37635,
-37676,
-37676,
-37704,
-37711,
-37728,
-37728,
-37737,
-37737,
-37737,
-37750,
-37757,
-37778,
-37785,
-37785,
-37819,
-37826,
-37833,
-37843,
-37850,
-37869,
-37914,
-37921,
-37935,
-37942,
-37949,
-37982,
-38013,
-38013,
-38013,
-38023,
-38057,
-38077,
-38097,
-38110,
-38117,
-38123,
-38133,
-38133,
-38133,
-38140,
-38140,
-38148,
-38159,
-38179,
-38192,
-38205,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38218,
-38225,
-38225,
-38230,
-38246,
-38258,
-38280,
-38287,
-38294,
-38294,
-38294,
-38301,
-38318,
-38318,
-38340,
-38371,
-38371,
-38384,
-38420,
-38440,
-38453,
-38481,
-38506,
-38522,
-38534,
-38559,
-38559,
-38559,
-38564,
-38564,
-38581,
-38604,
-38604,
-38611,
-38620,
-38626,
-38635,
-38635,
-38635,
-38666,
-38674,
-38688,
-38693,
-38710,
-38722,
-38722,
-38722,
-38729,
-38734,
-38752,
-38792,
-38818,
-38825,
-38861,
-38902,
-38934,
-38949,
-38949,
-38960,
-38969,
-38985,
-38985,
-38996,
-39013,
-39024,
-39024,
-39032,
-39061,
-39074,
-39089,
-39123,
-39123,
-39123,
-39140,
-39161,
-39180,
-39206,
-39215,
-39254,
-39261,
-39277,
-39284,
-39314,
-39314,
-39330,
-39340,
-39340,
-39371,
-39371,
-39392,
-39430,
-39430,
-39437,
-39444,
-39461,
-39468,
-39468,
-39485,
-39517,
-39524,
-39538,
-39543,
-39548,
-39555,
-39581,
-39588,
-39588,
-39609,
-39609,
-39616,
-39652,
-39670,
-39677,
-39677,
-39684,
-39691,
-39702,
-39717,
-39717,
-39717,
-39724,
-39749,
-39760,
-39766,
-39775,
-39791,
-39791,
-39814,
-39827,
-39827,
-39837,
-39859};
-
-static const char tldData[] = {
-"com.cn\0"
-"com.co\0"
-"hb.cn\0"
-"med.br\0conf.lv\0wallonie.museum\0"
-"namsos.no\0"
-"\xe7\xb6\xb2\xe7\xbb\x9c.hk\0farmers.museum\0rel.pl\0"
-"com.cu\0"
-"military.museum\0"
-"*.jm\0convent.museum\0cymru.museum\0malvik.no\0"
-"univ.sn\0"
-"gliding.aero\0"
-"wodzislaw.pl\0"
-"com.dm\0!pref.iwate.jp\0tran\xc3\xb8y.no\0pila.pl\0"
-"mb.it\0*.ke\0lib.ri.us\0"
-"com.ec\0*.kh\0tr\xc3\xb8gstad.no\0"
-"com.ee\0"
-"mobi.gp\0"
-"gran.no\0"
-"wa.gov.au\0"
-"com.dz\0kg.kr\0"
-"zoological.museum\0gjerstad.no\0haugesund.no\0kharkov.ua\0"
-"walbrzych.pl\0"
-"civilization.museum\0"
-"ha.no\0"
-"*.kw\0"
-"med.ec\0com.es\0"
-"med.ee\0otago.museum\0svelvik.no\0"
-"art.ht\0amber.museum\0elvendrell.museum\0rost.no\0"
-"jx.cn\0gratangen.no\0"
-"association.aero\0ca.it\0"
-"zaporizhzhe.ua\0"
-"com.fr\0"
-"szex.hu\0"
-"e-burg.ru\0"
-"com.ge\0bokn.no\0"
-"mordovia.ru\0"
-"com.gh\0*.mm\0"
-"com.gi\0z.se\0"
-"cahcesuolo.no\0"
-"hurdal.no\0joshkar-ola.ru\0"
-"cadaques.museum\0ma.us\0"
-"a.bg\0"
-"com.gn\0bozen.it\0tambov.ru\0"
-"*.gifu.jp\0*.tokyo.jp\0*.mt\0"
-"com.gp\0travel\0cc.tx.us\0"
-"com.gr\0hemne.no\0"
-"*.ni\0"
-"*.mz\0"
-"cc.il.us\0"
-"com.gy\0"
-"zj.cn\0oksnes.no\0museum.tt\0"
-"com.hk\0*.np\0"
-"rc.it\0baseball.museum\0"
-"com.hn\0exhibition.museum\0"
-"h\xc3\xa1""bmer.no\0"
-"com.hr\0"
-"fg.it\0stathelle.no\0defense.tn\0"
-"com.ht\0"
-"qld.gov.au\0*.nz\0"
-"davvenj\xc3\xa1rga.no\0*.om\0"
-"vang.no\0"
-"*.kumamoto.jp\0"
-"vercelli.it\0usenet.pl\0"
-"com.io\0stalbans.museum\0"
-"com.iq\0"
-"*.pg\0"
-"com.is\0klabu.no\0skiptvet.no\0"
-"med.ht\0field.museum\0"
-"gr.it\0gj\xc3\xb8vik.no\0tromsa.no\0lib.mi.us\0"
-"ca.na\0"
-"hagebostad.no\0k12.ma.us\0"
-"*.qa\0"
-"*.niigata.jp\0"
-"monzaebrianza.it\0com.jo\0comunica\xc3\xa7\xc3\xb5""es.museum\0"
-"gr.jp\0"
-"ballangen.no\0*.py\0"
-"scienceandindustry.museum\0"
-"nuoro.it\0com.kg\0"
-"com.ki\0"
-"im.it\0idv.tw\0"
-"*.akita.jp\0com.km\0r\xc3\xb8ros.no\0sopot.pl\0"
-"!pref.yamanashi.jp\0"
-"com.kp\0"
-"!pref.kochi.jp\0com.la\0"
-"com.lb\0"
-"com.lc\0stjordalshalsen.no\0sigdal.no\0cc.nm.us\0"
-"samnanger.no\0"
-"drobak.no\0"
-"vt.it\0"
-"catering.aero\0com.ky\0"
-"com.kz\0cc.ca.us\0"
-"com.lk\0"
-"grosseto.it\0mosvik.no\0"
-"namsskogan.no\0"
-"loten.no\0"
-"chirurgiens-dentistes.fr\0"
-"com.lr\0bremanger.no\0"
-"gs.cn\0"
-"com.lv\0lib.co.us\0"
-"com.mg\0"
-"passenger-association.aero\0"
-"com.ly\0yekaterinburg.ru\0"
-"vladivostok.ru\0"
-"com.mk\0beeldengeluid.museum\0"
-"com.ml\0"
-"art.pl\0"
-"com.mo\0"
-"britishcolumbia.museum\0tx.us\0"
-"com.na\0sakhalin.ru\0*.sv\0"
-"mc.it\0"
-"amsterdam.museum\0udm.ru\0"
-"com.mu\0"
-"com.mv\0com.nf\0"
-"com.mw\0com.ng\0il.us\0"
-"geometre-expert.fr\0com.mx\0"
-"med.ly\0com.my\0"
-"ag.it\0"
-"*.tr\0"
-"!pref.oita.jp\0"
-"hoyanger.no\0skedsmo.no\0"
-"com.nr\0turystyka.pl\0"
-"koebenhavn.museum\0"
-"quebec.museum\0"
-"stord.no\0*.uk\0"
-"act.au\0"
-"br.it\0cb.it\0gyeonggi.kr\0jobs.tt\0lib.hi.us\0"
-"*.ve\0"
-"*.saga.jp\0wildlife.museum\0com.pa\0"
-"monzabrianza.it\0sciencehistory.museum\0stange.no\0oskol.ru\0principe.st\0*.uy\0"
-"plaza.museum\0com.pe\0"
-"com.pf\0"
-"eigersund.no\0"
-"com.ph\0"
-"manx.museum\0marylhurst.museum\0"
-"md.ci\0pi.it\0schweiz.museum\0com.pk\0"
-"grp.lk\0fr\xc3\xb8ya.no\0com.pl\0press.se\0"
-"us.com\0"
-"b.bg\0cremona.it\0communication.museum\0art.sn\0"
-"med.pa\0"
-"com.pr\0"
-"com.ps\0"
-"com.pt\0"
-"k12.in.us\0"
-"ah.cn\0bahcavuotna.no\0"
-"sondrio.it\0arkhangelsk.ru\0"
-"cargo.aero\0"
-"council.aero\0"
-"museum.mv\0hattfjelldal.no\0spydeberg.no\0med.pl\0"
-"niepce.museum\0museum.mw\0"
-"anthropology.museum\0"
-"pharmacien.fr\0smola.no\0"
-"fin.ec\0"
-"selbu.no\0"
-"workinggroup.aero\0nm.us\0"
-"museum.no\0com.re\0"
-"cc.vt.us\0"
-"village.museum\0"
-"ca.us\0"
-"*.sapporo.jp\0"
-"teramo.it\0"
-"com.ro\0"
-"*.ye\0"
-"com.sa\0"
-"com.sb\0"
-"so.it\0com.sc\0"
-"jolster.no\0com.sd\0"
-"com.ru\0"
-"com.rw\0com.sg\0"
-"sydney.museum\0"
-"sa.edu.au\0"
-"tom.ru\0"
-"com.sl\0*.za\0"
-"\xe7\xbd\x91\xe7\xb5\xa1.hk\0naturbruksgymn.se\0com.sn\0"
-"assedic.fr\0com.so\0"
-"!pref.mie.jp\0*.yu\0"
-"med.sa\0"
-"newspaper.museum\0holmestrand.no\0dnepropetrovsk.ua\0"
-"christiansburg.museum\0roan.no\0"
-"pesaro-urbino.it\0med.sd\0com.st\0"
-"s\xc3\xb8gne.no\0"
-"nuernberg.museum\0"
-"*.zm\0"
-"com.sy\0"
-"*.nagano.jp\0com.tj\0"
-"nt.gov.au\0news.hu\0paderborn.museum\0"
-"boston.museum\0"
-"com.tn\0"
-"com.to\0"
-"broadcast.museum\0"
-"com.ua\0"
-"*.zw\0"
-"baikal.ru\0"
-"bykle.no\0com.tt\0"
-"verdal.no\0"
-"roros.no\0"
-"fi.cr\0carboniaiglesias.it\0chuvashia.ru\0com.tw\0"
-"k12.ca.us\0"
-"eidsvoll.no\0"
-"*.ishikawa.jp\0"
-"dolls.museum\0"
-"naval.museum\0"
-"karasjok.no\0tysvar.no\0"
-"bielawa.pl\0com.vc\0"
-"svalbard.no\0deatnu.no\0rnd.ru\0"
-"grandrapids.museum\0"
-"bauern.museum\0k12.pr.us\0"
-"press.ma\0"
-"*.kagawa.jp\0fribourg.museum\0przeworsk.pl\0com.vi\0"
-"com.uz\0"
-"babia-gora.pl\0"
-"com.vn\0"
-"med.pro\0"
-"suedtirol.it\0kursk.ru\0"
-"bonn.museum\0"
-"lt.it\0"
-"design.aero\0microlight.aero\0americanantiques.museum\0meland.no\0"
-"insurance.aero\0aarborte.no\0"
-"kh.ua\0"
-"macerata.it\0architecture.museum\0"
-"rovigo.it\0rawa-maz.pl\0"
-"store.nf\0levanger.no\0"
-"b\xc3\xa1jddar.no\0"
-"not.br\0"
-"com.ws\0"
-"!pref.kagawa.jp\0"
-"!omanpost.om\0"
-"vt.us\0"
-"gs.ah.no\0vladikavkaz.ru\0"
-"no.it\0"
-"in.na\0szkola.pl\0a.se\0"
-"aid.pl\0"
-"workshop.museum\0vegarshei.no\0"
-"sund.no\0"
-"bs.it\0flora.no\0"
-"agriculture.museum\0"
-"koeln.museum\0"
-"minnesota.museum\0k12.il.us\0"
-"froya.no\0"
-"aeroport.fr\0"
-"davvenjarga.no\0zgora.pl\0ivano-frankivsk.ua\0"
-"*.gunma.jp\0"
-"amot.no\0"
-"mus.br\0chungbuk.kr\0"
-"ggf.br\0lorenskog.no\0"
-"jeonbuk.kr\0"
-"k12.vi.us\0"
-"c.bg\0sande.more-og-romsdal.no\0"
-"perugia.it\0"
-"massa-carrara.it\0"
-"michigan.museum\0"
-"archaeology.museum\0mosj\xc3\xb8""en.no\0czest.pl\0koenig.ru\0\xe0\xb6\xbd\xe0\xb6\x82\xe0\xb6\x9a\xe0\xb7\x8f\0"
-"mobi.tt\0"
-"kraanghke.no\0"
-"cc.in.us\0"
-"re.it\0lib.vt.us\0"
-"dell-ogliastra.it\0"
-"s\xc3\xb8mna.no\0"
-"k12.wv.us\0"
-"gok.pk\0fh.se\0"
-"luzern.museum\0"
-"fi.it\0swidnica.pl\0"
-"cbg.ru\0"
-"latina.it\0"
-"vibovalentia.it\0"
-"modum.no\0"
-"safety.aero\0"
-"sp.it\0"
-"science.museum\0ah.no\0"
-"norddal.no\0"
-"cc.na\0"
-"re.kr\0"
-"dielddanuorri.no\0"
-"force.museum\0"
-"torino.it\0cc.md.us\0"
-"artanddesign.museum\0pisz.pl\0"
-"olsztyn.pl\0"
-"unsa.ba\0rade.no\0vinnica.ua\0"
-"in.rs\0astrakhan.ru\0"
-"sogne.no\0"
-"homebuilt.aero\0"
-"polkowice.pl\0"
-"hole.no\0health.vn\0"
-"fj.cn\0"
-"davvesiida.no\0"
-"vic.au\0"
-"kongsberg.no\0"
-"pub.sa\0"
-"vv.it\0"
-"!pref.tottori.jp\0"
-"*.sendai.jp\0in.th\0"
-"lib.pa.us\0"
-"chiropractic.museum\0"
-"mobi.na\0aca.pro\0"
-"konyvelo.hu\0sciencecenters.museum\0"
-"he.cn\0"
-"in.ua\0"
-"!city.nagoya.jp\0"
-"muenchen.museum\0"
-"psi.br\0"
-"maryland.museum\0"
-"!statecouncil.om\0"
-"tr\xc3\xa6na.no\0"
-"!pref.yamagata.jp\0jewishart.museum\0"
-"lu.it\0me.it\0"
-"chel.ru\0"
-"tatarstan.ru\0"
-"adult.ht\0in.us\0"
-"kafjord.no\0"
-"\xd7\x99\xd7\xa8\xd7\x95\xd7\xa9\xd7\x9c\xd7\x99\xd7\x9d.museum\0"
-"net.ac\0k12.ec\0"
-"net.ae\0bashkiria.ru\0"
-"net.af\0!omantel.om\0"
-"net.ag\0"
-"net.ai\0"
-"!pref.toyama.jp\0"
-"net.al\0timekeeping.museum\0"
-"net.an\0design.museum\0fin.tn\0"
-"ethnology.museum\0"
-"perso.ht\0asker.no\0b.se\0"
-"net.ba\0"
-"net.bb\0flanders.museum\0"
-"mincom.tn\0"
-"frana.no\0"
-"bt.it\0"
-"net.bh\0"
-"auto.pl\0"
-"net.az\0"
-"treviso.it\0"
-"war.museum\0"
-"net.bm\0"
-"langevag.no\0m\xc3\xa5lselv.no\0"
-"net.bo\0"
-"gol.no\0"
-"folkebibl.no\0"
-"net.br\0"
-"net.bs\0troandin.no\0saotome.st\0lib.tn.us\0"
-"md.us\0k12.ut.us\0"
-"d.bg\0cambridge.museum\0\xc3\xa5s.no\0"
-"net.ci\0"
-"net.bz\0"
-"sch.ae\0undersea.museum\0odda.no\0"
-"net.cn\0"
-"net.co\0c.la\0"
-"gliwice.pl\0"
-"aurskog-h\xc3\xb8land.no\0"
-"andria-trani-barletta.it\0"
-"net.cu\0loab\xc3\xa1t.no\0"
-"rep.kp\0"
-"\xe7\xbb\x84\xe7\xb9\x94.hk\0"
-"gallery.museum\0"
-"\xc3\xb8rland.no\0"
-"store.ro\0"
-"net.dm\0"
-"somna.no\0"
-"hemnes.no\0"
-"ringebu.no\0k12.ky.us\0"
-"net.ec\0"
-"dn.ua\0"
-"tarnobrzeg.pl\0"
-"soc.lk\0"
-"romsa.no\0"
-"bamble.no\0"
-"net.dz\0lutsk.ua\0"
-"barlettatraniandria.it\0ta.it\0countryestate.museum\0"
-"kaszuby.pl\0"
-"*.yamaguchi.jp\0cranbrook.museum\0store.st\0"
-"southcarolina.museum\0lib.md.us\0"
-"textile.museum\0"
-"cheltenham.museum\0hurum.no\0"
-"*.oita.jp\0"
-"shop.ht\0cc.me.us\0"
-"shop.hu\0turin.it\0"
-"louvre.museum\0"
-"k12.ar.us\0"
-"consulting.aero\0"
-"gv.ao\0"
-"sauherad.no\0"
-"gv.at\0net.ge\0"
-"ostre-toten.no\0lib.ok.us\0"
-"net.gg\0pilots.museum\0"
-"2000.hu\0geology.museum\0"
-"net.gn\0"
-"mazowsze.pl\0bir.ru\0"
-"net.gp\0"
-"net.gr\0"
-"oxford.museum\0"
-"per.la\0"
-"eastafrica.museum\0"
-"meeres.museum\0"
-"net.gy\0*.shizuoka.jp\0"
-"\xe5\x95\x86\xe6\xa5\xad.tw\0"
-"net.hk\0"
-"net.hn\0"
-"philadelphiaarea.museum\0"
-"osen.no\0"
-"net.ht\0net.id\0"
-"fundacio.museum\0"
-"j\xc3\xb8rpeland.no\0"
-"\xe6\x95\x99\xe8\x82\xb2.hk\0"
-"divtasvuodna.no\0"
-"student.aero\0sch.gg\0net.im\0"
-"\xe7\xbd\x91\xe7\xbb\x9c.cn\0net.in\0"
-"net.iq\0"
-"net.ir\0"
-"net.is\0"
-"net.je\0"
-"kepno.pl\0lapy.pl\0"
-"per.nf\0"
-"gov\0*.shimane.jp\0"
-"artcenter.museum\0"
-"k\xc3\xa5""fjord.no\0"
-"net.jo\0"
-"eu.int\0"
-"c.se\0"
-"net.kg\0"
-"ce.it\0net.ki\0"
-"sch.id\0os.hedmark.no\0"
-"columbus.museum\0"
-"arteducation.museum\0"
-"net.kn\0"
-"kr.com\0"
-"net.la\0bushey.museum\0cc.gu.us\0"
-"net.lb\0"
-"net.lc\0"
-"gs.bu.no\0"
-"e164.arpa\0"
-"chieti.it\0labour.museum\0"
-"sch.ir\0creation.museum\0krodsherad.no\0"
-"net.ky\0"
-"net.kz\0me.us\0"
-"e.bg\0sch.je\0net.lk\0"
-"zlg.br\0suwalki.pl\0"
-"\xe5\x80\x8b\xe4\xba\xba.hk\0net.ma\0"
-"net.lr\0"
-"sch.jo\0notaires.km\0net.me\0"
-"net.lv\0karate.museum\0"
-"net.ly\0karm\xc3\xb8y.no\0"
-"rg.it\0"
-"net.mk\0"
-"net.ml\0evenes.no\0"
-"ngo.lk\0net.mo\0egyptian.museum\0"
-"marine.ru\0"
-"realestate.pl\0"
-"net.mu\0"
-"net.mv\0net.nf\0"
-"net.mw\0net.ng\0gda.pl\0"
-"net.mx\0"
-"freemasonry.museum\0net.my\0enebakk.no\0"
-"karlsoy.no\0"
-"\xe7\xbd\x91\xe7\xbb\x9c.hk\0\xc3\xb8rskog.no\0"
-"randaberg.no\0"
-"club.aero\0"
-"certification.aero\0sr.it\0"
-"center.museum\0so.gov.pl\0"
-"caa.aero\0"
-"sch.lk\0tvedestrand.no\0"
-"net.nr\0"
-"luroy.no\0"
-"aukra.no\0s\xc3\xa1lat.no\0lib.me.us\0"
-"ddr.museum\0"
-"york.museum\0stryn.no\0k12.nm.us\0"
-"per.sg\0"
-"judaica.museum\0"
-"verona.it\0"
-"agdenes.no\0"
-"cng.br\0sch.ly\0"
-"net.pa\0"
-"author.aero\0"
-"naturalhistory.museum\0steiermark.museum\0bu.no\0"
-"sn\xc3\xa5sa.no\0net.pe\0"
-"net.ph\0"
-"savannahga.museum\0batsfjord.no\0lib.oh.us\0"
-"net.pk\0"
-"net.pl\0"
-"net.pn\0"
-"washingtondc.museum\0"
-"net.pr\0"
-"net.ps\0"
-"net.pt\0"
-"nordkapp.no\0"
-"emergency.aero\0krokstadelva.no\0"
-"satx.museum\0ngo.ph\0omsk.ru\0"
-"texas.museum\0"
-"ngo.pl\0"
-"mantova.it\0gu.us\0"
-"!pref.shiga.jp\0isa.us\0"
-"usa.museum\0"
-"gb.net\0k12.vi\0"
-"iveland.no\0"
-"tempio-olbia.it\0"
-"net.sa\0"
-"net.sb\0"
-"works.aero\0net.sc\0komvux.se\0"
-"net.sd\0"
-"net.ru\0"
-"0.bg\0"
-"forlicesena.it\0net.rw\0net.sg\0"
-"klodzko.pl\0"
-"detroit.museum\0wegrow.pl\0"
-"net.sl\0"
-"glogow.pl\0"
-"store.bb\0air.museum\0"
-"net.so\0"
-"katowice.pl\0"
-"nsk.ru\0"
-"pisa.it\0eid.no\0"
-"net.st\0"
-"film.hu\0"
-"tuva.ru\0d.se\0"
-"net.th\0"
-"net.sy\0"
-"viterbo.it\0tsaritsyn.ru\0perso.sn\0net.tj\0"
-"lib.gu.us\0"
-"plc.co.im\0sec.ps\0"
-"r\xc3\xa1hkker\xc3\xa1vju.no\0kazimierz-dolny.pl\0net.tn\0"
-"net.to\0"
-"veterinaire.km\0"
-"net.ua\0"
-"info.ht\0net.tt\0"
-"info.hu\0"
-"exchange.aero\0"
-"sch.sa\0net.tw\0"
-"andriatranibarletta.it\0perso.tn\0"
-"f.bg\0malselv.no\0"
-"net.vc\0"
-"trana.no\0"
-"ns.ca\0"
-"net.vi\0"
-"lucca.it\0oristano.it\0"
-"usarts.museum\0net.vn\0"
-"gon.pk\0"
-"pl.ua\0"
-"eastcoast.museum\0"
-"novara.it\0"
-"k12.ks.us\0"
-"dp.ua\0"
-"nesseby.no\0"
-"!pref.wakayama.jp\0"
-"repbody.aero\0"
-"jamison.museum\0lugansk.ua\0"
-"ss.it\0"
-"alessandria.it\0"
-"hadsel.no\0net.ws\0"
-"\xe0\xae\x9a\xe0\xae\xbf\xe0\xae\x99\xe0\xaf\x8d\xe0\xae\x95\xe0\xae\xaa\xe0\xaf\x8d\xe0\xae\xaa\xe0\xaf\x82\xe0\xae\xb0\xe0\xaf\x8d\0"
-"veterinaire.fr\0leirfjord.no\0"
-"massacarrara.it\0north.museum\0"
-"project.museum\0"
-"other.nf\0"
-"k12.nh.us\0"
-"mat.br\0artgallery.museum\0"
-"sr.gov.pl\0"
-"gamvik.no\0"
-"info.ec\0lancashire.museum\0"
-"fm.br\0ltd.co.im\0"
-"americana.museum\0southwest.museum\0cc.ak.us\0"
-"enna.it\0lunner.no\0"
-"v\xc3\xa5gan.no\0"
-"mari.ru\0"
-"accident-investigation.aero\0"
-"sor-aurdal.no\0lib.ny.us\0"
-"novosibirsk.ru\0"
-"bjugn.no\0"
-"n\xc3\xa6r\xc3\xb8y.no\0ostrowwlkp.pl\0"
-"info.bb\0foundation.museum\0"
-"brand.se\0"
-"info.at\0!pref.akita.jp\0l\xc3\xb8ten.no\0"
-"coal.museum\0miners.museum\0"
-"glass.museum\0"
-"info.az\0"
-"frog.museum\0szczytno.pl\0nov.ru\0"
-"sunndal.no\0"
-"gen.in\0"
-"gx.cn\0"
-"web.co\0*.mie.jp\0hobol.no\0\xe5\x8f\xb0\xe6\xb9\xbe\0"
-"logistics.aero\0plo.ps\0"
-"erotika.hu\0"
-"torsken.no\0"
-"exeter.museum\0"
-"info.co\0"
-"selje.no\0"
-"storfjord.no\0"
-"barum.no\0lind\xc3\xa5s.no\0"
-"leasing.aero\0"
-"championship.aero\0fst.br\0"
-"lierne.no\0"
-"!gobiernoelectronico.ar\0""1.bg\0"
-"corporation.museum\0"
-"al.it\0*.miyagi.jp\0"
-"*.aomori.jp\0"
-"\xd8\xa7\xd9\x84\xd8\xa7\xd8\xb1\xd8\xaf\xd9\x86\0"
-"amursk.ru\0"
-"vestvagoy.no\0"
-"\xd8\xa7\xdb\x8c\xd8\xb1\xd8\xa7\xd9\x86.ir\0cc.fl.us\0"
-"os.hordaland.no\0"
-"pistoia.it\0"
-"tver.ru\0e.se\0"
-"res.in\0*.yamagata.jp\0syzran.ru\0"
-"capebreton.museum\0sandnessj\xc3\xb8""en.no\0"
-"ternopil.ua\0"
-"shop.pl\0"
-"tank.museum\0"
-"m\xc3\xa5s\xc3\xb8y.no\0"
-"potenza.it\0time.museum\0"
-"mjondalen.no\0"
-"eng.br\0nedre-eiker.no\0"
-"air-surveillance.aero\0"
-"nt.au\0am.br\0pn.it\0"
-"oystre-slidre.no\0ug.gov.pl\0"
-"g.bg\0nesodden.no\0vologda.ru\0"
-"parma.it\0tula.ru\0"
-"*.nara.jp\0ak.us\0"
-"nt.ca\0konin.pl\0"
-"kiev.ua\0"
-"skierv\xc3\xa1.no\0vestre-toten.no\0"
-"ri.it\0botanical.museum\0farsund.no\0veg\xc3\xa5rshei.no\0dagestan.ru\0"
-"ind.br\0k-uralsk.ru\0"
-"rahkkeravju.no\0cmw.ru\0"
-"canada.museum\0"
-"fm.it\0"
-"cc.wi.us\0"
-"web.id\0aver\xc3\xb8y.no\0"
-"dudinka.ru\0"
-"baghdad.museum\0fitjar.no\0grane.no\0"
-"gs.fm.no\0"
-"sumy.ua\0"
-"al.no\0"
-"westfalen.museum\0"
-"oregon.museum\0"
-"bruxelles.museum\0elk.pl\0"
-"planetarium.museum\0sn\xc3\xa5""ase.no\0"
-"s\xc3\xb8rreisa.no\0"
-"gs.st.no\0skien.no\0"
-"bible.museum\0ivanovo.ru\0"
-"avellino.it\0"
-"tgory.pl\0"
-"family.museum\0"
-"ppg.br\0k12.as.us\0"
-"trader.aero\0gorlice.pl\0"
-"cc.al.us\0"
-"ogliastra.it\0"
-"is.it\0lib.nv.us\0"
-"dr.na\0"
-"media.hu\0nesna.no\0fl.us\0"
-"uri.arpa\0"
-"bjerkreim.no\0"
-"charter.aero\0"
-"genova.it\0"
-"it.ao\0botany.museum\0hapmir.no\0"
-"educational.museum\0"
-"helsinki.museum\0"
-"memorial.museum\0"
-"web.lk\0pharmacy.museum\0"
-"aircraft.aero\0appspot.com\0"
-"ferrara.it\0beskidy.pl\0"
-"hi.cn\0"
-"taxi.aero\0flekkefjord.no\0"
-"varoy.no\0"
-"ragusa.it\0ambulance.museum\0"
-"can.museum\0"
-"*.osaka.jp\0isleofman.museum\0fm.no\0warmia.pl\0"
-"educator.aero\0asmatart.museum\0"
-"mi.it\0"
-"kutno.pl\0"
-"skedsmokorset.no\0"
-"2.bg\0"
-"*.kagoshima.jp\0km.ua\0"
-"!city.sendai.jp\0"
-"web.nf\0st.no\0cc.ri.us\0"
-"reggiocalabria.it\0"
-"wi.us\0"
-"ancona.it\0newjersey.museum\0nnov.ru\0"
-"f.se\0"
-"ind.in\0"
-"info.vn\0"
-"andoy.no\0"
-"ch.it\0fredrikstad.no\0guovdageaidnu.no\0"
-"fjaler.no\0"
-"sa.com\0"
-"gs.nt.no\0"
-"masfjorden.no\0"
-"pordenone.it\0"
-"po.it\0basel.museum\0"
-"chambagri.fr\0"
-"h.bg\0web.pk\0"
-"london.museum\0"
-"sciencecenter.museum\0\xe0\xb9\x84\xe0\xb8\x97\xe0\xb8\xa2\0"
-"unbi.ba\0augustow.pl\0"
-"wolomin.pl\0"
-"notaires.fr\0tcm.museum\0al.us\0"
-"nu.ca\0!pref.nagano.jp\0"
-"info.tn\0"
-"lib.wa.us\0"
-"ed.ao\0info.tt\0"
-"barreau.bj\0"
-"k12.wy.us\0"
-"pp.az\0gop.pk\0"
-"int\0"
-"l\xc3\xb8renskog.no\0podhale.pl\0"
-"voagat.no\0"
-"telekommunikation.museum\0"
-"qld.au\0"
-"te.it\0freiburg.museum\0snasa.no\0"
-"gjemnes.no\0"
-"sejny.pl\0"
-"media.pl\0"
-"skjak.no\0"
-"watchandclock.museum\0"
-"ed.ci\0pacific.museum\0"
-"theater.museum\0info.ro\0"
-"uk.com\0"
-"campobasso.it\0aquarium.museum\0tysv\xc3\xa6r.no\0"
-"kragero.no\0"
-"windmill.museum\0info.sd\0"
-"sologne.museum\0sande.m\xc3\xb8re-og-romsdal.no\0"
-"nt.no\0cc.mi.us\0"
-"ed.cr\0"
-"academy.museum\0zachpomor.pl\0"
-"tananger.no\0v\xc3\xa1rgg\xc3\xa1t.no\0ri.us\0"
-"federation.aero\0"
-"web.tj\0"
-"matta-varjjat.no\0"
-"steigen.no\0"
-"local\0akrehamn.no\0"
-"!pref.chiba.jp\0info.pk\0"
-"info.pl\0""6bone.pl\0"
-"klepp.no\0kherson.ua\0"
-"ketrzyn.pl\0info.pr\0"
-"sweden.museum\0"
-"lardal.no\0"
-"!retina.ar\0gz.cn\0"
-"barletta-trani-andria.it\0vikna.no\0"
-"bearalv\xc3\xa1hki.no\0"
-"broker.aero\0gov.nc.tr\0"
-"info.na\0k12.fl.us\0"
-"hembygdsforbund.museum\0"
-"entertainment.aero\0jerusalem.museum\0l\xc3\xa6rdal.no\0"
-"hitra.no\0sogndal.no\0"
-"farmequipment.museum\0info.mv\0info.nf\0\xc3\xa5lg\xc3\xa5rd.no\0"
-"la-spezia.it\0"
-"skanland.no\0fam.pk\0"
-"skole.museum\0"
-"art.museum\0"
-"presidio.museum\0"
-"3.bg\0public.museum\0"
-"h\xc3\xb8yanger.no\0zagan.pl\0"
-"an.it\0"
-"philadelphia.museum\0info.nr\0"
-"pesarourbino.it\0g\xc3\xa1ivuotna.no\0"
-"poltava.ua\0"
-"nt.ro\0"
-"station.museum\0"
-"mi.th\0"
-"altoadige.it\0"
-"nu.it\0"
-"usculture.museum\0g.se\0"
-"h\xc3\xa1mm\xc3\xa1rfeasta.no\0"
-"daegu.kr\0info.la\0"
-"dovre.no\0"
-"ci.it\0horology.museum\0"
-"bergbau.museum\0"
-"press.museum\0"
-"gangwon.kr\0"
-"!city.kitakyushu.jp\0sor-varanger.no\0cc.hi.us\0"
-"fuossko.no\0"
-"zp.ua\0"
-"american.museum\0"
-"fl\xc3\xa5.no\0mi.us\0"
-"i.bg\0"
-"od.ua\0"
-"encyclopedic.museum\0"
-"ind.tn\0"
-"midatlantic.museum\0"
-"newyork.museum\0"
-"castres.museum\0"
-"act.edu.au\0"
-"topology.museum\0"
-"ed.jp\0"
-"of.by\0"
-"iris.arpa\0inf.br\0askim.no\0pyatigorsk.ru\0"
-"nord-fron.no\0nsn.us\0"
-"beardu.no\0"
-"agrar.hu\0corvette.museum\0chtr.k12.ma.us\0"
-"figueres.museum\0"
-"!pref.gunma.jp\0medizinhistorisches.museum\0"
-"tjeldsund.no\0"
-"nebraska.museum\0"
-"bellevue.museum\0"
-"abo.pa\0k12.al.us\0"
-"info.ki\0"
-"inf.cu\0sv.it\0"
-"jfk.museum\0"
-"!city.osaka.jp\0swinoujscie.pl\0"
-"bydgoszcz.pl\0"
-"!city.kyoto.jp\0"
-"uvic.museum\0"
-"madrid.museum\0steinkjer.no\0"
-"lib.ma.us\0"
-"sirdal.no\0"
-"n\xc3\xb8tter\xc3\xb8y.no\0"
-"taranto.it\0starnberg.museum\0"
-"vic.gov.au\0pvt.ge\0pors\xc3\xa1\xc5\x8bgu.no\0"
-"naroy.no\0ris\xc3\xb8r.no\0"
-"va.it\0salem.museum\0starachowice.pl\0"
-"!nawrastelecom.om\0"
-"town.museum\0te.ua\0"
-"se.net\0"
-"kemerovo.ru\0"
-"lerdal.no\0"
-"gs.va.no\0"
-"kms.ru\0"
-"consulado.st\0"
-"haram.no\0"
-"tysnes.no\0"
-"!pref.ibaraki.jp\0hamburg.museum\0"
-"\xc3\xa5rdal.no\0"
-"airline.aero\0"
-"crew.aero\0newhampshire.museum\0"
-"muenster.museum\0"
-"aerodrome.aero\0"
-"heroy.nordland.no\0belau.pw\0"
-"kamchatka.ru\0"
-"b\xc3\xa5""d\xc3\xa5""ddj\xc3\xa5.no\0lillehammer.no\0hi.us\0"
-"hk.cn\0"
-"!city.kobe.jp\0berlevag.no\0"
-"ardal.no\0"
-"askoy.no\0"
-"vardo.no\0"
-"fyresdal.no\0"
-"sassari.it\0"
-"video.hu\0drammen.no\0"
-"lyngen.no\0nakhodka.ru\0"
-"ip6.arpa\0games.hu\0"
-"online.museum\0"
-"k12.sd.us\0"
-"4.bg\0sebastopol.ua\0"
-"ao.it\0atlanta.museum\0"
-"lebork.pl\0"
-"ravenna.it\0"
-"railway.museum\0songdalen.no\0"
-"!pref.shimane.jp\0delaware.museum\0ed.pw\0"
-"f\xc3\xb8rde.no\0"
-"living.museum\0"
-"juif.museum\0"
-"lomza.pl\0"
-"h.se\0"
-"!bl.uk\0"
-"portland.museum\0\xe7\xb5\x84\xe7\xb9\x94.tw\0"
-"stj\xc3\xb8rdal.no\0"
-"lecce.it\0"
-"bz.it\0"
-"farmstead.museum\0va.no\0"
-"express.aero\0!nacion.ar\0"
-"presse.km\0gs.of.no\0"
-"\xe5\x8f\xb0\xe7\x81\xa3\0"
-"og.ao\0gyeongbuk.kr\0vestv\xc3\xa5g\xc3\xb8y.no\0"
-"prd.fr\0"
-"pp.ru\0pp.se\0"
-"forum.hu\0!pref.saga.jp\0"
-"kvalsund.no\0"
-"!city.kawasaki.jp\0n\xc3\xa5\xc3\xa5mesjevuemie.no\0"
-"j.bg\0"
-"vlaanderen.museum\0"
-"cc.va.us\0"
-"\xd8\xa7\xd9\x8a\xd8\xb1\xd8\xa7\xd9\x86.ir\0alabama.museum\0"
-"school.museum\0her\xc3\xb8y.m\xc3\xb8re-og-romsdal.no\0"
-"\xc3\xa5seral.no\0"
-"traniandriabarletta.it\0"
-"flog.br\0"
-"presse.ml\0"
-"k\xc3\xa1r\xc3\xa1\xc5\xa1johka.no\0"
-"historisch.museum\0"
-"farm.museum\0palmsprings.museum\0oslo.no\0dyroy.no\0stranda.no\0"
-"gs.rl.no\0r\xc3\xa5""de.no\0"
-"bomlo.no\0s\xc3\xb8rum.no\0"
-"jan-mayen.no\0ivgu.no\0"
-"coop\0"
-"agr.br\0k12.ak.us\0"
-"!nic.ar\0catanzaro.it\0fusa.no\0"
-"hu.com\0"
-"inf.mk\0"
-"vet.br\0"
-"k12.mt.us\0k12.nd.us\0"
-"vlog.br\0\xe5\x85\xac\xe5\x8f\xb8.cn\0sandnessjoen.no\0"
-"lib.az.us\0"
-"nsw.edu.au\0of.no\0\xc3\xb8stre-toten.no\0"
-"*.okinawa.jp\0"
-"vb.it\0"
-"asso.fr\0firenze.it\0"
-"trieste.it\0"
-"\xe5\x85\xac\xe5\x8f\xb8.hk\0"
-"museet.museum\0"
-"prd.km\0"
-"navuotna.no\0lib.ca.us\0"
-"cc.nv.us\0"
-"asso.gp\0"
-"meraker.no\0"
-"h\xc3\xa1pmir.no\0"
-"i.ph\0"
-"sx.cn\0jeonnam.kr\0"
-"halden.no\0"
-"fed.us\0"
-"medio-campidano.it\0tsk.ru\0"
-"barcelona.museum\0"
-"giessen.museum\0roma.museum\0"
-"hl.cn\0"
-"\xe0\xae\x87\xe0\xae\xb2\xe0\xae\x99\xe0\xaf\x8d\xe0\xae\x95\xe0\xaf\x88\0"
-"biz.bb\0benevento.it\0rl.no\0bygland.no\0"
-"port.fr\0asso.ht\0prd.mg\0"
-"biz.at\0"
-"tra.kp\0"
-"*.aichi.jp\0khabarovsk.ru\0"
-"campidano-medio.it\0"
-"biz.az\0"
-"newmexico.museum\0va.us\0"
-"finearts.museum\0"
-"murmansk.ru\0"
-"\xc3\xb8rsta.no\0radom.pl\0k12.sc.us\0"
-"5.bg\0kvinesdal.no\0"
-"ap.it\0"
-"*.fukushima.jp\0"
-"asso.bj\0"
-"mad.museum\0"
-"lebesby.no\0"
-"og.it\0glas.museum\0sauda.no\0"
-"i.se\0"
-"k12.tx.us\0"
-"asso.ci\0mk.ua\0"
-"cesena-forli.it\0"
-"lowicz.pl\0"
-"k12.id.us\0"
-"tas.gov.au\0"
-"lukow.pl\0"
-"utazas.hu\0"
-"maritimo.museum\0bjark\xc3\xb8y.no\0"
-"adm.br\0"
-"pr.it\0lib.vi.us\0"
-"bergamo.it\0k12.va.us\0"
-"k.bg\0"
-"railroad.museum\0"
-"!british-library.uk\0"
-"cincinnati.museum\0"
-"sorreisa.no\0"
-"asso.dz\0!nel.uk\0"
-"rm.it\0"
-"nv.us\0"
-"nx.cn\0gos.pk\0"
-"vic.edu.au\0"
-"biella.it\0tjome.no\0"
-"r\xc3\xb8yken.no\0"
-"beiarn.no\0"
-"qc.ca\0"
-"georgia.museum\0square.museum\0"
-"labor.museum\0omasvuotna.no\0cc.la.us\0"
-"br.com\0reggioemilia.it\0"
-"kristiansund.no\0"
-"sorum.no\0"
-"orsta.no\0"
-"furniture.museum\0surrey.museum\0eng.pro\0"
-"asn.lv\0balat.no\0"
-"lavangen.no\0sld.pa\0"
-"fla.no\0k12.ms.us\0k12.nc.us\0"
-"bardu.no\0"
-"donostia.museum\0"
-"club.tw\0"
-"elburg.museum\0"
-"gs.hl.no\0lodingen.no\0"
-"samara.ru\0"
-"vc.it\0*.nagasaki.jp\0"
-"fosnes.no\0"
-"fuel.aero\0"
-"qc.com\0"
-"skjervoy.no\0"
-"bill.museum\0kv\xc3\xa6""fjord.no\0"
-"skydiving.aero\0*.tokushima.jp\0"
-"!congresodelalengua3.ar\0laquila.it\0k12.ct.us\0"
-"gorge.museum\0linz.museum\0sherbrooke.museum\0"
-"tranoy.no\0ing.pa\0"
-"ptz.ru\0"
-"kr.it\0prato.it\0stat.no\0"
-"\xd0\xb8\xd0\xba\xd0\xbe\xd0\xbc.museum\0"
-"cosenza.it\0"
-"stj\xc3\xb8rdalshalsen.no\0"
-"finland.museum\0leka.no\0cc.pr.us\0"
-"historichouses.museum\0s\xc3\xa1l\xc3\xa1t.no\0"
-"venice.it\0"
-"biz.ki\0"
-"g\xc3\xa1ls\xc3\xa1.no\0"
-"\xe7\xbb\x84\xe7\xbb\x87.hk\0"
-"*.yamanashi.jp\0"
-"rad\xc3\xb8y.no\0"
-"6.bg\0"
-"fareast.ru\0"
-"paragliding.aero\0ba.it\0aq.it\0"
-"sk\xc3\xa5nland.no\0"
-"its.me\0"
-"us.na\0"
-"hl.no\0cc.ga.us\0"
-"ac\0granvin.no\0"
-"ad\0qld.edu.au\0!city.sapporo.jp\0"
-"ae\0"
-"af\0"
-"ag\0crotone.it\0"
-"dallas.museum\0"
-"ai\0brussels.museum\0"
-"dali.museum\0"
-"la.us\0"
-"al\0salzburg.museum\0"
-"am\0"
-"an\0cl.it\0"
-"ao\0"
-"aq\0ba\0"
-"bb\0"
-"as\0lajolla.museum\0"
-"at\0"
-"be\0"
-"bf\0inderoy.no\0snz.ru\0"
-"aw\0bg\0"
-"ax\0bh\0cim.br\0ltd.gi\0biz.mv\0"
-"bi\0xz.cn\0\xe7\xb5\x84\xe7\xb9\x94.hk\0biz.mw\0"
-"az\0bj\0"
-"bm\0tranibarlettaandria.it\0naamesjevuemie.no\0"
-"chattanooga.museum\0"
-"bo\0"
-"l.bg\0"
-"ca\0"
-"br\0stateofdelaware.museum\0"
-"bs\0cc\0"
-"cd\0biz.nr\0"
-"cf\0berlev\xc3\xa5g.no\0"
-"bw\0cg\0snaase.no\0"
-"ch\0harvestcelebration.museum\0ck.ua\0"
-"by\0ci\0"
-"bz\0bahccavuotna.no\0"
-"cl\0yuzhno-sakhalinsk.ru\0"
-"cm\0halsa.no\0lyngdal.no\0"
-"cn\0"
-"co\0rn.it\0childrens.museum\0frankfurt.museum\0"
-"cr\0"
-"pskov.ru\0"
-"cu\0de\0"
-"cv\0fr.it\0lib.ky.us\0"
-"aseral.no\0kvam.no\0"
-"cx\0hellas.museum\0"
-"hof.no\0"
-"cz\0dj\0k12.la.us\0"
-"dk\0moscow.museum\0"
-"sosnowiec.pl\0"
-"dm\0biz.pk\0"
-"schokoladen.museum\0biz.pl\0"
-"far.br\0arna.no\0tynset.no\0"
-"even\xc3\xa1\xc5\xa1\xc5\xa1i.no\0"
-"ec\0"
-"biz.pr\0"
-"ee\0celtic.museum\0"
-"scientist.aero\0modern.museum\0"
-"pr.us\0"
-"dz\0"
-"mj\xc3\xb8ndalen.no\0s\xc3\xb8r-odal.no\0"
-"!nic.tr\0"
-"conference.aero\0vestnes.no\0k12.mn.us\0"
-"!pref.hiroshima.jp\0"
-"es\0trapani.it\0"
-"fermo.it\0vard\xc3\xb8.no\0"
-"eu\0gs.hm.no\0r\xc3\xb8""d\xc3\xb8y.no\0stordal.no\0"
-"gc.ca\0!nhs.uk\0"
-"jgora.pl\0"
-"fi\0stjordal.no\0"
-"fm\0!mediaphone.om\0"
-"kirov.ru\0pvt.k12.ma.us\0"
-"fo\0"
-"ga\0hyllestad.no\0"
-"gov.ac\0fr\0andriabarlettatrani.it\0ga.us\0"
-"gov.ae\0gd\0estate.museum\0"
-"gov.af\0ge\0tolga.no\0"
-"gf\0asso.re\0cc.oh.us\0"
-"gg\0florida.museum\0"
-"presse.ci\0gh\0"
-"gi\0k12.dc.us\0"
-"ltd.lk\0orland.no\0"
-"gov.al\0"
-"gl\0tokke.no\0"
-"hanggliding.aero\0gm\0"
-"hareid.no\0"
-"gov.ba\0tj.cn\0gp\0"
-"gov.bb\0gq\0"
-"gov.as\0gr\0agrigento.it\0lc.it\0"
-"gs\0kalmykia.ru\0aero.tt\0"
-"gov.bf\0"
-"county.museum\0"
-"gov.bh\0hn.cn\0gw\0"
-"gov.az\0gy\0assn.lk\0guernsey.museum\0"
-"hk\0"
-"gov.bm\0h\xc3\xa6gebostad.no\0biz.tj\0"
-"hm\0computer.museum\0"
-"gov.bo\0hn\0kl\xc3\xa6""bu.no\0"
-"pulawy.pl\0"
-"gov.br\0"
-"trd.br\0gov.bs\0hr\0reggio-calabria.it\0historyofscience.museum\0lipetsk.ru\0"
-"gov.cd\0*.nagoya.jp\0"
-"ht\0id\0spjelkavik.no\0"
-"hu\0ie\0aero.mv\0"
-"marketplace.aero\0mn.it\0biz.tt\0"
-"gov.by\0saintlouis.museum\0mer\xc3\xa5ker.no\0"
-"gov.bz\0"
-"7.bg\0gov.cl\0virtual.museum\0"
-"gov.cm\0vennesla.no\0kr.ua\0"
-"gov.cn\0im\0ar.it\0galsa.no\0rovno.ua\0"
-"gov.co\0in\0"
-"io\0limanowa.pl\0"
-"iq\0k12.ga.us\0"
-"ir\0"
-"riik.ee\0is\0\xc3\xa1laheadju.no\0"
-"gov.cu\0it\0hawaii.museum\0seaport.museum\0"
-"je\0pubol.museum\0hm.no\0"
-"gov.cx\0"
-"*.chiba.jp\0"
-"*.kawasaki.jp\0"
-"k.se\0"
-"gov.dm\0"
-"aland.fi\0vik.no\0"
-"yk.ca\0jo\0kobierzyce.pl\0"
-"jp\0biz.vn\0"
-"presse.fr\0lib.il.us\0\xe9\xa6\x99\xe6\xb8\xaf\0"
-"gov.ec\0"
-"transport.museum\0bronnoy.no\0"
-"slg.br\0gov.ee\0asso.nc\0bievat.no\0"
-"nyny.museum\0"
-"kg\0"
-"mo-i-rana.no\0"
-"gov.dz\0ki\0"
-"monmouth.museum\0"
-"suldal.no\0"
-"bc.ca\0km\0zt.ua\0"
-"pt.it\0kn\0"
-"fineart.museum\0"
-"la\0"
-"kr\0gulen.no\0"
-"m.bg\0mo.cn\0lc\0alaheadju.no\0g\xc3\xa1\xc5\x8bgaviika.no\0"
-"nowaruda.pl\0cc.ut.us\0"
-"br\xc3\xb8nn\xc3\xb8y.no\0"
-"ky\0li\0overhalla.no\0"
-"kz\0khv.ru\0"
-"lk\0"
-"artdeco.museum\0"
-"ma\0fortworth.museum\0kostroma.ru\0"
-"ro.it\0kirkenes.no\0vestby.no\0"
-"urbino-pesaro.it\0ls\0mc\0alstahaug.no\0"
-"blog.br\0gov.ge\0lt\0md\0"
-"lu\0me\0botanicgarden.museum\0"
-"gov.gg\0lv\0oh.us\0"
-"gov.gh\0mg\0valley.museum\0"
-"gov.gi\0mh\0"
-"ly\0sandiego.museum\0"
-"mk\0"
-"ml\0"
-"gov.gn\0rollag.no\0naklo.pl\0"
-"mn\0"
-"mo\0"
-"mp\0leirvik.no\0"
-"gov.gr\0mq\0na\0cc.ks.us\0"
-"mr\0"
-"ms\0nc\0"
-"valer.hedmark.no\0"
-"mu\0ne\0"
-"mv\0nf\0"
-"mw\0"
-"mx\0nord-odal.no\0jur.pro\0"
-"my\0"
-"gov.hk\0name.hr\0"
-"nl\0"
-"astronomy.museum\0lib.nm.us\0"
-"catania.it\0"
-"no\0"
-"skjerv\xc3\xb8y.no\0"
-"k12.ne.us\0"
-"monza-e-della-brianza.it\0!pref.fukushima.jp\0nr\0"
-"gov.ie\0"
-"stuttgart.museum\0nu\0cc.mn.us\0"
-"karasjohka.no\0"
-"engine.aero\0bearalvahki.no\0"
-"oyer.no\0"
-"ve.it\0"
-"gov.im\0froland.no\0cc.ar.us\0"
-"gov.in\0magadan.ru\0"
-"pescara.it\0"
-"gov.iq\0usdecorativearts.museum\0"
-"gov.ir\0pa\0"
-"gov.is\0"
-"gov.it\0lavagis.no\0"
-"gov.je\0"
-"naustdal.no\0pe\0k12.or.us\0"
-"gd.cn\0carraramassa.it\0pf\0"
-"ph\0"
-"cc.ny.us\0"
-"rissa.no\0"
-"info\0pk\0pomorze.pl\0"
-"pl\0"
-"gov.jo\0asso.km\0pn\0"
-"*.okayama.jp\0cieszyn.pl\0"
-"freight.aero\0"
-"pr\0"
-"narvik.no\0ps\0"
-"!pref.aichi.jp\0elverum.no\0pt\0"
-"edunet.tn\0"
-"gov.kg\0"
-"flatanger.no\0marker.no\0pw\0"
-"gov.ki\0nuremberg.museum\0"
-"aip.ee\0"
-"gov.km\0"
-"gov.kn\0"
-"gov.kp\0"
-"rieti.it\0gov.la\0bajddar.no\0"
-"gov.lb\0aviation.museum\0"
-"gov.lc\0"
-"asso.mc\0"
-"re\0"
-"ut.us\0"
-"sa.gov.au\0gov.ky\0"
-"mo.it\0gov.kz\0"
-"gov.lk\0"
-"iraq.museum\0"
-"badajoz.museum\0"
-"8.bg\0inder\xc3\xb8y.no\0"
-"monticello.museum\0ro\0ks.ua\0"
-"gov.ma\0svizzera.museum\0"
-"gov.lr\0sa\0"
-"matera.it\0sb\0"
-"gov.lt\0rs\0sc\0"
-"gov.me\0sd\0"
-"gov.lv\0ru\0se\0"
-"gov.mg\0"
-"rw\0sg\0"
-"gov.ly\0assisi.museum\0kids.museum\0sh\0"
-"si\0"
-"gov.mk\0"
-"gov.ml\0sk\0"
-"sl\0"
-"gov.mn\0airguard.museum\0sm\0"
-"gov.mo\0l.se\0sn\0"
-"so\0"
-"gov.mr\0ks.us\0"
-"name.az\0sr\0"
-"naturhistorisches.museum\0tc\0"
-"trainer.aero\0cn.it\0urbinopesaro.it\0gov.mu\0nativeamerican.museum\0st\0td\0"
-"gov.mv\0su\0"
-"trentino.it\0gov.mw\0gov.ng\0tf\0"
-"tg\0"
-"co.ae\0venezia.it\0gov.my\0th\0"
-"!pref.ehime.jp\0sy\0"
-"co.ag\0lewismiller.museum\0ostrowiec.pl\0sz\0tj\0"
-"tk\0"
-"motorcycle.museum\0tl\0"
-"birdart.museum\0trogstad.no\0tm\0"
-"tn\0"
-"humanities.museum\0to\0"
-"pu.it\0gov.nr\0ua\0lib.ut.us\0"
-"co.ao\0"
-"co.ba\0trondheim.no\0tt\0"
-"in-addr.arpa\0tempioolbia.it\0!city.yokohama.jp\0mn.us\0"
-"n.bg\0schoenbrunn.museum\0tv\0"
-"co.at\0aremark.no\0tw\0ug\0"
-"jus.br\0"
-"co.bi\0bialowieza.pl\0ar.us\0"
-"audnedaln.no\0kustanai.ru\0"
-"va\0"
-"us\0vc\0"
-"newport.museum\0"
-"kopervik.no\0gov.ph\0vg\0"
-"ny.us\0vi\0"
-"co.bw\0finn\xc3\xb8y.no\0gov.pk\0uz\0"
-"honefoss.no\0gov.pl\0lanbib.se\0"
-"co.ci\0"
-"gov.pn\0intl.tn\0"
-"act.gov.au\0vn\0"
-"television.museum\0gov.pr\0"
-"sykkylven.no\0v\xc3\xa5ler.hedmark.no\0gov.ps\0"
-"gov.pt\0"
-"co.cr\0vu\0"
-"legnica.pl\0"
-"sa.au\0"
-"bjarkoy.no\0"
-"openair.museum\0birkenes.no\0lib.nj.us\0"
-"fylkesbibl.no\0holt\xc3\xa5len.no\0"
-"iz.hr\0"
-"ws\0"
-"oceanographique.museum\0"
-"b\xc3\xa1id\xc3\xa1r.no\0cc.mo.us\0"
-"\xc3\xb8ygarden.no\0"
-"contemporary.museum\0"
-"gb.com\0cc.as.us\0"
-"belluno.it\0gov.sa\0"
-"gov.sb\0"
-"gov.rs\0gov.sc\0"
-"gov.sd\0"
-"!pref.nagasaki.jp\0gov.ru\0"
-"asia\0"
-"sa.cr\0gov.rw\0gov.sg\0"
-"kuzbass.ru\0"
-"gs.vf.no\0"
-"gov.sl\0"
-"norfolk.museum\0"
-"k12.de.us\0"
-"mil\0"
-"rendalen.no\0"
-"gov.st\0"
-"agro.pl\0"
-"orkdal.no\0"
-"le.it\0gov.sy\0"
-"gov.tj\0"
-"co.gg\0nore-og-uvdal.no\0v\xc3\xa5ler.\xc3\xb8stfold.no\0"
-"gov.tl\0"
-"gov.tn\0"
-"gov.to\0"
-"kids.us\0"
-"equipment.aero\0gov.ua\0"
-"!city.niigata.jp\0gov.tt\0"
-"sel.no\0"
-"l\xc3\xa4ns.museum\0"
-"gov.tw\0"
-"rennebu.no\0"
-"egersund.no\0"
-"medecin.km\0"
-"co.gy\0"
-"!mecon.ar\0"
-"berlin.museum\0"
-"carrara-massa.it\0"
-"9.bg\0"
-"pri.ee\0gov.vc\0"
-"at.it\0"
-"muosat.no\0"
-"co.id\0"
-"co.hu\0"
-"etne.no\0"
-"\xc3\xa1lt\xc3\xa1.no\0"
-"gov.vn\0"
-"modelling.aero\0"
-"co.im\0"
-"co.in\0\xc3\xa5krehamn.no\0m.se\0"
-"gouv.fr\0*.kitakyushu.jp\0"
-"narviika.no\0"
-"rennes\xc3\xb8y.no\0"
-"co.ir\0afjord.no\0"
-"lea\xc5\x8bgaviika.no\0buryatia.ru\0"
-"co.it\0coastaldefence.museum\0"
-"co.je\0vf.no\0"
-"osteroy.no\0"
-"uslivinghistory.museum\0"
-"aerobatic.aero\0"
-"mesaverde.museum\0mining.museum\0"
-"a\xc3\xa9roport.ci\0gov.ws\0"
-"co.jp\0copenhagen.museum\0"
-"pv.it\0"
-"r\xc3\xb8mskog.no\0"
-"vossevangen.no\0porsanger.no\0"
-"salat.no\0mo.us\0"
-"o.bg\0imperia.it\0carrier.museum\0"
-"carbonia-iglesias.it\0"
-"as.us\0"
-"alvdal.no\0"
-"state.museum\0mandal.no\0cn.ua\0"
-"cuneo.it\0"
-"gouv.ht\0"
-"!city.okayama.jp\0co.kr\0"
-"co.lc\0"
-"sa.it\0"
-"donna.no\0"
-"sortland.no\0"
-"tomsk.ru\0"
-"birthplace.museum\0l\xc3\xb8""dingen.no\0"
-"ge.it\0orenburg.ru\0"
-"cn.com\0"
-"co.ma\0"
-"co.ls\0skaun.no\0name.vn\0"
-"navigation.aero\0"
-"cagliari.it\0co.me\0portal.museum\0"
-"gouv.bj\0"
-"udine.it\0"
-"engineer.aero\0"
-"szczecin.pl\0"
-"wales.museum\0"
-"co.na\0bo.telemark.no\0"
-"austin.museum\0"
-"k12.mo.us\0"
-"co.mu\0"
-"gouv.ci\0"
-"co.mw\0"
-"esp.br\0"
-"naturalhistorymuseum.museum\0"
-"mosjoen.no\0"
-"solund.no\0"
-"name.tj\0"
-"sand\xc3\xb8y.no\0"
-"kunstunddesign.museum\0"
-"cartoonart.museum\0collection.museum\0gsm.pl\0"
-"aure.no\0"
-"!pref.yamaguchi.jp\0historical.museum\0"
-"name.tt\0"
-"england.museum\0valle.no\0"
-"cc.ok.us\0"
-"salangen.no\0"
-"gloppen.no\0"
-"cc.co.us\0"
-"contemporaryart.museum\0"
-"tas.edu.au\0"
-"trading.aero\0"
-"mazury.pl\0"
-"!pref.aomori.jp\0co.pl\0"
-"opoczno.pl\0"
-"*.kobe.jp\0co.pn\0"
-"oppegard.no\0"
-"co.pw\0"
-"saltdal.no\0smolensk.ru\0"
-"na.it\0\xc4\x8d\xc3\xa1hcesuolo.no\0"
-"vgs.no\0evenassi.no\0"
-"parachuting.aero\0jl.cn\0maritime.museum\0bd.se\0"
-"badaddja.no\0"
-"bergen.no\0"
-"brussel.museum\0"
-"avoues.fr\0"
-"cesenaforli.it\0"
-"oregontrail.museum\0"
-"ullensaker.no\0"
-"jobs\0"
-"accident-prevention.aero\0"
-"n.se\0"
-"association.museum\0california.museum\0"
-"cultural.museum\0co.rs\0"
-"zoology.museum\0"
-"pruszkow.pl\0"
-"control.aero\0nt.edu.au\0net\0komforb.se\0"
-"lincoln.museum\0aurland.no\0name.pr\0co.rw\0"
-"ostroleka.pl\0"
-"isernia.it\0"
-"tm.fr\0"
-"gs.ol.no\0"
-"nb.ca\0marnardal.no\0"
-"williamsburg.museum\0"
-"!jet.uk\0"
-"suisse.museum\0\xc3\xa5""fjord.no\0flakstad.no\0"
-"karmoy.no\0"
-"yn.cn\0chesapeakebay.museum\0"
-"nsw.au\0"
-"amur.ru\0co.st\0"
-"imb.br\0siellak.no\0\xe7\xb6\xb2\xe8\xb7\xaf.tw\0"
-"name.na\0"
-"co.th\0"
-"p.bg\0"
-"co.sz\0co.tj\0"
-"name.mv\0\xc3\xa5lesund.no\0lib.in.us\0"
-"lucerne.museum\0naumburg.museum\0"
-"society.museum\0name.my\0"
-"tinn.no\0"
-"co.tt\0"
-"unj\xc3\xa1rga.no\0"
-"co.ug\0"
-"lib.wy.us\0"
-"co.tz\0"
-"ass.km\0"
-"ok.us\0"
-"tm.hu\0kongsvinger.no\0"
-"ibestad.no\0"
-"juedisches.museum\0co.us\0"
-"cq.cn\0"
-"rs.ba\0"
-"wa.edu.au\0co.vi\0"
-"co.uz\0"
-"health.museum\0"
-"grue.no\0"
-"automotive.museum\0journalism.museum\0settlement.museum\0"
-"qh.cn\0interactive.museum\0"
-"snillfjord.no\0!national-library-scotland.uk\0"
-"balsfjord.no\0lib.nh.us\0"
-"kolobrzeg.pl\0"
-"gs.tm.no\0"
-"h\xc3\xb8nefoss.no\0"
-"ol.no\0"
-"music.museum\0moareke.no\0"
-"b\xc3\xb8.nordland.no\0"
-"name.mk\0lier.no\0"
-"eidfjord.no\0"
-"sc.cn\0tm.km\0"
-"jelenia-gora.pl\0sanok.pl\0"
-"intelligence.museum\0"
-"srv.br\0elblag.pl\0"
-"judygarland.museum\0"
-"padua.it\0"
-"k12.co.us\0"
-"lindesnes.no\0"
-"name.jo\0izhevsk.ru\0"
-"yorkshire.museum\0mel\xc3\xb8y.no\0"
-"tm.mc\0lib.pr.us\0"
-"hjartdal.no\0"
-"tm.mg\0"
-"bari.it\0milano.it\0"
-"lg.jp\0"
-"zgrad.ru\0"
-"sm\xc3\xb8la.no\0"
-"communications.museum\0"
-"arts.co\0seoul.kr\0engerdal.no\0"
-"oster\xc3\xb8y.no\0"
-"\xe6\x95\x8e\xe8\x82\xb2.hk\0foggia.it\0verran.no\0"
-"orskog.no\0voronezh.ru\0kv.ua\0"
-"av.it\0"
-"tm.no\0nissedal.no\0"
-"historisches.museum\0gs.mr.no\0"
-"medecin.fr\0"
-"montreal.museum\0"
-"o.se\0"
-"!metro.tokyo.jp\0sola.no\0"
-"k12.tn.us\0"
-"floro.no\0"
-"milan.it\0*.shiga.jp\0"
-"berkeley.museum\0"
-"maintenance.aero\0"
-"ws.na\0"
-"lindas.no\0cc.ia.us\0"
-"brescia.it\0embroidery.museum\0"
-"arezzo.it\0tm.pl\0"
-"r\xc3\xa6lingen.no\0"
-"burghof.museum\0"
-"rec.br\0"
-"q.bg\0"
-"!nawras.om\0"
-"hammarfeasta.no\0"
-"moss.no\0"
-"on.ca\0"
-"gouv.rw\0"
-"luxembourg.museum\0"
-"rec.co\0british.museum\0"
-"reggio-emilia.it\0"
-"gouv.sn\0lib.wv.us\0"
-"avocat.fr\0"
-"simbirsk.ru\0"
-"jar.ru\0"
-"monza-brianza.it\0"
-"tm.ro\0"
-"imageandsound.museum\0"
-"jpn.com\0mr.no\0"
-"siracusa.it\0"
-"norilsk.ru\0tm.se\0"
-"tn.it\0"
-"jeju.kr\0"
-"!pref.fukuoka.jp\0"
-"*.hyogo.jp\0portlligat.museum\0"
-"!pref.osaka.jp\0"
-"siena.it\0sc.kr\0omaha.museum\0saskatchewan.museum\0"
-"phoenix.museum\0vanylven.no\0"
-"botanicalgarden.museum\0"
-"turek.pl\0"
-"vagsoy.no\0"
-"riodejaneiro.museum\0"
-"vi.it\0"
-"uy.com\0"
-"kristiansand.no\0"
-"sd.cn\0trento.it\0"
-"muncie.museum\0"
-"berg.no\0meldal.no\0"
-"nes.buskerud.no\0"
-"saratov.ru\0"
-"gs.oslo.no\0"
-"harstad.no\0vaga.no\0"
-"research.museum\0"
-"brunel.museum\0ia.us\0"
-"test.tj\0"
-"columbia.museum\0"
-"ms.it\0stockholm.museum\0"
-"reklam.hu\0"
-"pomorskie.pl\0lg.ua\0"
-"bg.it\0historicalsociety.museum\0rns.tn\0"
-"mallorca.museum\0surgut.ru\0cc.sc.us\0"
-"ushistory.museum\0"
-"palana.ru\0"
-"snoasa.no\0"
-"naturalsciences.museum\0"
-"yaroslavl.ru\0"
-"unjarga.no\0"
-"p.se\0"
-"ingatlan.hu\0"
-"irc.pl\0"
-"savona.it\0"
-"cr.it\0"
-"test.ru\0cc.tn.us\0"
-"ms.kr\0museumvereniging.museum\0"
-"time.no\0k12.ia.us\0"
-"vladimir.ru\0"
-"correios-e-telecomunica\xc3\xa7\xc3\xb5""es.museum\0"
-"gouv.km\0nationalfirearms.museum\0"
-"m\xc3\xa1latvuopmi.no\0"
-"aero\0yosemite.museum\0"
-"r.bg\0school.na\0"
-"cc.vi.us\0"
-"*.wakayama.jp\0"
-"beauxarts.museum\0averoy.no\0ullensvang.no\0bar.pro\0"
-"!city.hiroshima.jp\0"
-"b\xc3\xa1hccavuotna.no\0"
-"frosta.no\0"
-"gdynia.pl\0"
-"medical.museum\0"
-"embaixada.st\0"
-"balsan.it\0vantaa.museum\0"
-"za.net\0"
-"!city.saitama.jp\0lib.ks.us\0"
-"fnd.br\0"
-"ru.com\0se.com\0hol.no\0modalen.no\0"
-"gouv.ml\0chukotka.ru\0"
-"malopolska.pl\0"
-"mansion.museum\0"
-"iki.fi\0children.museum\0"
-"cyber.museum\0rec.nf\0mo\xc3\xa5reke.no\0"
-"to.it\0"
-"hasvik.no\0"
-"\xc3\xb8yer.no\0"
-"arts.ro\0sc.ug\0"
-"lib.ar.us\0"
-"sc.tz\0cc.ms.us\0cc.nc.us\0"
-"etc.br\0poznan.pl\0"
-"cnt.br\0viking.museum\0"
-"*.miyazaki.jp\0"
-"melhus.no\0"
-"skodje.no\0vevelstad.no\0"
-"sc.us\0"
-"upow.gov.pl\0"
-"!city.fukuoka.jp\0brandywinevalley.museum\0natuurwetenschappen.museum\0tranby.no\0"
-"bahn.museum\0msk.ru\0"
-"delmenhorst.museum\0"
-"russia.museum\0fuoisku.no\0"
-"shell.museum\0"
-"r\xc3\xa1isa.no\0"
-"hs.kr\0udmurtia.ru\0"
-"palermo.it\0"
-"pilot.aero\0"
-"tn.us\0"
-"priv.hu\0"
-"li.it\0"
-"kr\xc3\xa5""anghke.no\0mosreg.ru\0"
-"lib.fl.us\0"
-"plants.museum\0"
-"ulsan.kr\0national.museum\0"
-"mil.ac\0!pref.nara.jp\0surgeonshall.museum\0"
-"mil.ae\0santacruz.museum\0vi.us\0"
-"wlocl.pl\0"
-"mt.it\0napoli.it\0alaska.museum\0arts.nf\0"
-"missoula.museum\0"
-"rec.ro\0"
-"mil.al\0"
-"marburg.museum\0waw.pl\0"
-"pharmaciens.km\0indianapolis.museum\0larsson.museum\0"
-"cc.sd.us\0"
-"mil.ba\0mobi\0"
-"indianmarket.museum\0"
-"recreation.aero\0padova.it\0"
-"varese.it\0parti.se\0"
-"mil.az\0"
-"mil.bo\0!pref.kagoshima.jp\0khmelnitskiy.ua\0"
-"rygge.no\0"
-"os\xc3\xb8yro.no\0"
-"mil.br\0"
-"cs.it\0"
-"austevoll.no\0fjell.no\0"
-"mil.by\0"
-"!pref.tokushima.jp\0org\0"
-"mil.cn\0gs.svalbard.no\0"
-"mil.co\0"
-"pz.it\0lib.va.us\0\xd1\x80\xd1\x84\0"
-"\xe4\xb8\xaa\xe4\xba\xba.hk\0ms.us\0nc.us\0k12.wi.us\0"
-"s.bg\0drangedal.no\0"
-"en.it\0"
-"culturalcenter.museum\0"
-"house.museum\0divttasvuotna.no\0"
-"fhs.no\0"
-"circus.museum\0"
-"priv.at\0"
-"mil.ec\0"
-"ruovat.no\0"
-"midsund.no\0vagan.no\0"
-"casadelamoneda.museum\0"
-"bristol.museum\0"
-"and.museum\0"
-"ascolipiceno.it\0computerhistory.museum\0vyatka.ru\0"
-"uhren.museum\0"
-"lahppi.no\0"
-"*.yokohama.jp\0cody.museum\0lib.al.us\0"
-"colonialwilliamsburg.museum\0indian.museum\0cc.ky.us\0"
-"tp.it\0biev\xc3\xa1t.no\0"
-"can.br\0royken.no\0"
-"id.ir\0"
-"mediocampidano.it\0tromso.no\0"
-"kartuzy.pl\0k12.ok.us\0"
-"*.saitama.jp\0stjohn.museum\0m\xc3\xa1tta-v\xc3\xa1rjjat.no\0"
-"mil.ge\0trani-barletta-andria.it\0"
-"lib.as.us\0"
-"swiebodzin.pl\0cc.mt.us\0cc.nd.us\0"
-"mil.gh\0"
-"science-fiction.museum\0\xd9\x82\xd8\xb7\xd8\xb1\0"
-"airtraffic.aero\0"
-"konskowola.pl\0"
-"scienceandhistory.museum\0nysa.pl\0sd.us\0"
-"balestrand.no\0"
-"oygarden.no\0"
-"her\xc3\xb8y.nordland.no\0"
-"!pref.ishikawa.jp\0strand.no\0"
-"\xe7\xb5\x84\xe7\xbb\x87.hk\0mil.hn\0"
-"gob.bo\0volda.no\0"
-"losangeles.museum\0larvik.no\0"
-"university.museum\0"
-"cc.dc.us\0"
-"mil.id\0"
-"sorfold.no\0"
-"watch-and-clock.museum\0"
-"flor\xc3\xb8.no\0"
-"nittedal.no\0oppeg\xc3\xa5rd.no\0"
-"k12.ri.us\0"
-"gob.cl\0"
-"komi.ru\0"
-"government.aero\0mil.in\0"
-"mil.iq\0id.lv\0"
-"culture.museum\0"
-"id.ly\0"
-"raholt.no\0"
-"lubin.pl\0grozny.ru\0"
-"kchr.ru\0"
-"nikolaev.ua\0"
-"lib.sd.us\0"
-"de.com\0"
-"mil.jo\0"
-"*.kanagawa.jp\0gaular.no\0miasta.pl\0"
-"bi.it\0rnu.tn\0uzhgorod.ua\0"
-"idrett.no\0v\xc3\xa5gs\xc3\xb8y.no\0"
-"wroclaw.pl\0"
-"res.aero\0ne.jp\0mil.kg\0"
-"\xc3\xa5mli.no\0"
-"education.museum\0"
-"dgca.aero\0"
-"mil.km\0"
-"trolley.museum\0"
-"cci.fr\0r.se\0"
-"archaeological.museum\0"
-"monzaedellabrianza.it\0mil.kr\0"
-"gob.es\0kvafjord.no\0ky.us\0"
-"lecco.it\0"
-"ct.it\0"
-"magazine.aero\0"
-"operaunite.com\0ne.kr\0"
-"mil.kz\0skoczow.pl\0"
-"nf.ca\0"
-"western.museum\0"
-"kunst.museum\0gaivuotna.no\0karpacz.pl\0spb.ru\0cc.id.us\0"
-"slask.pl\0"
-"youth.museum\0"
-"adv.br\0campidanomedio.it\0!songfest.om\0"
-"geelvinck.museum\0\xd8\xa7\xd9\x85\xd8\xa7\xd8\xb1\xd8\xa7\xd8\xaa\0"
-"mil.lv\0"
-"fie.ee\0mil.mg\0mt.us\0nd.us\0k12.vt.us\0"
-"t.bg\0ushuaia.museum\0"
-"off.ai\0"
-"irkutsk.ru\0"
-"stor-elvdal.no\0tourism.tn\0"
-"penza.ru\0"
-"bj.cn\0\xe4\xb8\xad\xe5\x9b\xbd\0"
-"civilwar.museum\0mil.mv\0opole.pl\0"
-"nes.akershus.no\0"
-"mil.my\0karelia.ru\0"
-"como.it\0sande.vestfold.no\0"
-"\xe4\xb8\xad\xe5\x9c\x8b\0"
-"gob.hn\0lib.la.us\0"
-"mil.no\0cc.wv.us\0"
-"boleslawiec.pl\0"
-"!pref.niigata.jp\0gs.sf.no\0dc.us\0k12.mi.us\0"
-"museum\0dep.no\0kv\xc3\xa6nangen.no\0l\xc3\xa1hppi.no\0"
-"film.museum\0"
-"frei.no\0"
-"notodden.no\0risor.no\0"
-"messina.it\0"
-"eidsberg.no\0"
-"krakow.pl\0lib.mt.us\0lib.nd.us\0"
-"rauma.no\0"
-"mulhouse.museum\0"
-"sibenik.museum\0grong.no\0mil.pe\0"
-"budejju.no\0k12.nv.us\0"
-"stavanger.no\0mil.ph\0"
-"forli-cesena.it\0"
-"naples.it\0cc.ne.us\0"
-"s\xc3\xb8r-aurdal.no\0"
-"mil.pl\0"
-"vibo-valentia.it\0ski.museum\0siedlce.pl\0"
-"bus.museum\0"
-"tozsde.hu\0"
-"!pref.shizuoka.jp\0santabarbara.museum\0"
-"zhitomir.ua\0"
-"pro.az\0"
-"ne.pw\0"
-"pro.br\0orkanger.no\0b\xc3\xb8.telemark.no\0"
-"roma.it\0cc.ct.us\0"
-"heritage.museum\0giske.no\0"
-"!pref.kumamoto.jp\0prof.pr\0"
-"*.kochi.jp\0"
-"andria-barletta-trani.it\0*.toyama.jp\0sveio.no\0"
-"id.us\0"
-"bolt.hu\0"
-"fetsund.no\0porsgrunn.no\0"
-"iglesias-carbonia.it\0"
-"sf.no\0"
-"mil.ru\0"
-"from.hr\0asnes.no\0mil.rw\0"
-"alesund.no\0sos.pl\0"
-"livorno.it\0"
-"crafts.museum\0"
-"aquila.it\0"
-"vega.no\0"
-"jewelry.museum\0"
-"sk\xc3\xa1nit.no\0chita.ru\0"
-"pro.ec\0"
-"fortmissoula.museum\0j\xc3\xb8lster.no\0"
-"pro\0mil.st\0"
-"busan.kr\0lib.ga.us\0"
-"dellogliastra.it\0"
-"aosta.it\0chungnam.kr\0gob.mx\0"
-"mil.sy\0k12.hi.us\0"
-"mil.tj\0"
-"ulan-ude.ru\0mil.to\0wv.us\0"
-"luster.no\0volgograd.ru\0"
-"pa.it\0kommunalforbund.se\0lib.tx.us\0"
-"s.se\0"
-"qsl.br\0"
-"mil.tw\0"
-"est.pr\0ens.tn\0"
-"lib.id.us\0"
-"mil.tz\0"
-"uscountryestate.museum\0"
-"agents.aero\0"
-"\xc3\xb8vre-eiker.no\0ne.ug\0"
-"pb.ao\0"
-"gob.pa\0ne.tz\0"
-"tur.br\0"
-"mil.vc\0"
-"or.at\0gob.pe\0"
-"s\xc3\xb8r-fron.no\0"
-"or.bi\0ne.us\0"
-"u.bg\0gob.pk\0"
-"stavern.no\0"
-"brindisi.it\0"
-"aknoluokta.no\0"
-"!pref.kyoto.jp\0tydal.no\0"
-"plc.ly\0muos\xc3\xa1t.no\0"
-"or.ci\0hamaroy.no\0priv.pl\0"
-"vestre-slidre.no\0gniezno.pl\0"
-"\xe7\xae\x87\xe4\xba\xba.hk\0"
-"andebu.no\0"
-"nieruchomosci.pl\0\xd8\xa7\xd9\x84\xd8\xb3\xd8\xb9\xd9\x88\xd8\xaf\xd9\x8a\xd8\xa9\0"
-"or.cr\0pro.ht\0bolzano.it\0"
-"ct.us\0k12.md.us\0"
-"za.org\0"
-"!icnet.uk\0"
-"localhistory.museum\0"
-"firm.ht\0"
-"lel.br\0tr.it\0kvanangen.no\0"
-"sondre-land.no\0t\xc3\xb8nsberg.no\0vefsn.no\0"
-"nature.museum\0yamal.ru\0"
-"rv.ua\0"
-"lans.museum\0lib.ne.us\0"
-"lur\xc3\xb8y.no\0"
-"eu.com\0firm.in\0"
-"hjelmeland.no\0"
-"gs.tr.no\0"
-"casino.hu\0essex.museum\0tourism.pl\0"
-"rennesoy.no\0"
-"priv.no\0"
-"baths.museum\0mytis.ru\0"
-"tingvoll.no\0"
-"cc.az.us\0"
-"sh.cn\0"
-"!pref.miyazaki.jp\0s\xc3\xb8rfold.no\0"
-"aurskog-holand.no\0malatvuopmi.no\0"
-"lib.ct.us\0"
-"cc.pa.us\0"
-"pa.gov.pl\0"
-"firm.co\0cc.de.us\0"
-"nrw.museum\0"
-"daejeon.kr\0livinghistory.museum\0"
-"gildeskal.no\0lund.no\0"
-"\xc3\xb8ksnes.no\0stavropol.ru\0"
-"b\xc3\xa6rum.no\0r\xc3\xb8yrvik.no\0"
-"osoyro.no\0"
-"priv.me\0sula.no\0!parliament.uk\0"
-"nationalheritage.museum\0"
-"jaworzno.pl\0"
-"dinosaur.museum\0"
-"garden.museum\0trust.museum\0"
-"turen.tn\0"
-"kautokeino.no\0"
-"pro.na\0"
-"gorizia.it\0"
-"siljan.no\0"
-"or.id\0pro.mv\0"
-"bieszczady.pl\0www.ro\0"
-"lib.ee\0antiques.museum\0brasil.museum\0tr.no\0"
-"aejrie.no\0"
-"!pref.hokkaido.jp\0"
-"schlesisches.museum\0"
-"huissier-justice.fr\0or.it\0"
-"t.se\0"
-"environment.museum\0"
-"vindafjord.no\0"
-"edu.ac\0or.jp\0"
-"tree.museum\0"
-"groundhandling.aero\0edu.af\0"
-"rochester.museum\0sanfrancisco.museum\0"
-"ebiz.tw\0"
-"kirovograd.ua\0"
-"edu.al\0"
-"edu.an\0\xc3\xa1k\xc5\x8boluokta.no\0v\xc3\xa5g\xc3\xa5.no\0"
-"v.bg\0"
-"edu.ba\0"
-"edu.bb\0nesset.no\0"
-"hornindal.no\0pro.pr\0"
-"or.kr\0"
-"az.us\0"
-"edu.bh\0volkenkunde.museum\0"
-"edu.bi\0"
-"edu.az\0"
-"b\xc3\xb8mlo.no\0"
-"edu.bm\0"
-"edu.bo\0tyumen.ru\0"
-"edu.br\0"
-"edu.bs\0pa.us\0"
-"alto-adige.it\0whaling.museum\0"
-"*.iwate.jp\0"
-"edu.ci\0law.pro\0"
-"edu.bz\0de.us\0"
-"lib.ak.us\0"
-"edu.cn\0"
-"edu.co\0"
-"laspezia.it\0"
-"baidar.no\0"
-"ts.it\0"
-"or.na\0"
-"edu.cu\0hotel.lk\0"
-"show.aero\0or.mu\0"
-"sandnes.no\0"
-"museumcenter.museum\0"
-"edu.dm\0kazan.ru\0"
-"biz\0caltanissetta.it\0odessa.ua\0k12.oh.us\0"
-"crimea.ua\0"
-"research.aero\0lom.no\0"
-"edu.ec\0florence.it\0clock.museum\0sshn.se\0"
-"edu.ee\0game.tw\0"
-"!pref.okinawa.jp\0"
-"ilawa.pl\0"
-"edu.dz\0indiana.museum\0"
-"gs.jan-mayen.no\0"
-"publ.pt\0"
-"nom.ad\0"
-"skanit.no\0gdansk.pl\0k12.pa.us\0"
-"nom.ag\0edu.es\0"
-"if.ua\0"
-"pro.tt\0lib.de.us\0"
-"environmentalconservation.museum\0cc.or.us\0"
-"bern.museum\0nat.tn\0"
-"rubtsovsk.ru\0"
-"!educ.ar\0masoy.no\0"
-"bologna.it\0"
-"\xc3\xa5snes.no\0fhv.se\0"
-"*.tottori.jp\0radoy.no\0"
-"romskog.no\0"
-"malbork.pl\0"
-"olbiatempio.it\0"
-"edu.ge\0"
-"edu.gh\0"
-"edu.gi\0"
-"or.pw\0"
-"hob\xc3\xb8l.no\0"
-"nom.br\0edu.gn\0virginia.museum\0mbone.pl\0!nls.uk\0"
-"seljord.no\0pro.vn\0"
-"edu.gp\0"
-"edu.gr\0"
-"!uba.ar\0!pref.saitama.jp\0"
-"greta.fr\0gs.aa.no\0kvinnherad.no\0"
-"lib.sc.us\0"
-"js.cn\0nom.co\0edu.hk\0"
-"lesja.no\0"
-"bl.it\0"
-"edu.hn\0\xc3\xb8ystre-slidre.no\0mari-el.ru\0"
-"hotel.hu\0"
-"rindal.no\0"
-"edu.ht\0"
-"!pref.miyagi.jp\0"
-"midtre-gauldal.no\0"
-"xj.cn\0australia.museum\0"
-"ab.ca\0salvadordali.museum\0olawa.pl\0"
-"pc.it\0"
-"u.se\0"
-"edu.in\0b\xc3\xa1l\xc3\xa1t.no\0"
-"ln.cn\0alta.no\0"
-"chelyabinsk.ru\0"
-"edu.iq\0"
-"ontario.museum\0"
-"edu.is\0"
-"edu.it\0"
-"b\xc3\xa5tsfjord.no\0"
-"trysil.no\0or.th\0"
-"utsira.no\0"
-"nom.es\0edu.jo\0fhsk.se\0"
-"bale.museum\0"
-"w.bg\0"
-"lillesand.no\0"
-"edu.kg\0"
-"amusement.aero\0"
-"edu.ki\0"
-"fauske.no\0or.ug\0"
-"int.az\0askvoll.no\0eidskog.no\0cv.ua\0"
-"algard.no\0"
-"edu.km\0or.tz\0"
-"nom.fr\0edu.kn\0"
-"*.ibaraki.jp\0hoylandet.no\0"
-"int.bo\0edu.kp\0"
-"edu.la\0"
-"si.it\0edu.lb\0travel.pl\0"
-"edu.lc\0mx.na\0n\xc3\xa1vuotna.no\0ovre-eiker.no\0"
-"aa.no\0!siemens.om\0"
-"sciences.museum\0or.us\0"
-"cat\0"
-"edu.ky\0"
-"int.ci\0edu.kz\0firm.ro\0cc.wy.us\0"
-"edu.lk\0vaapste.no\0"
-"!pref.tochigi.jp\0"
-"int.co\0podlasie.pl\0"
-"edu.lr\0"
-"karikatur.museum\0jamal.ru\0"
-"gjovik.no\0krager\xc3\xb8.no\0k12.az.us\0"
-"edu.me\0"
-"ud.it\0edu.lv\0entomology.museum\0"
-"edu.mg\0moskenes.no\0"
-"\xe6\x94\xbf\xe5\xba\x9c.hk\0edu.ly\0"
-"stpetersburg.museum\0"
-"edu.mk\0"
-"edu.ml\0nordreisa.no\0"
-"!pref.fukui.jp\0lib.ms.us\0lib.nc.us\0"
-"edu.mn\0\xd9\x81\xd9\x84\xd8\xb3\xd8\xb7\xd9\x8a\xd9\x86\0"
-"fot.br\0edu.mo\0"
-"iron.museum\0"
-"asti.it\0annefrank.museum\0stv.ru\0cc.nh.us\0"
-"edu.mv\0"
-"lodi.it\0edu.mw\0edu.ng\0"
-"gwangju.kr\0edu.mx\0"
-"edu.my\0"
-"soundandvision.museum\0"
-"lenvik.no\0"
-"ballooning.aero\0"
-"name\0"
-"jogasz.hu\0frogn.no\0"
-"history.museum\0"
-"consultant.aero\0edu.nr\0"
-"manchester.museum\0"
-"*.hiroshima.jp\0"
-"pol.dz\0"
-"*.tochigi.jp\0heimatunduhren.museum\0"
-"!pref.kanagawa.jp\0"
-"firm.nf\0edu.pa\0"
-"coop.ht\0pc.pl\0"
-"chicago.museum\0"
-"vn.ua\0"
-"edu.pe\0"
-"tana.no\0edu.pf\0"
-"edu.ph\0"
-"nom.km\0"
-"travel.tt\0"
-"edu.pk\0"
-"experts-comptables.fr\0edu.pl\0bryansk.ru\0"
-"edu.pn\0"
-"evje-og-hornnes.no\0warszawa.pl\0"
-"ac.ae\0"
-"edu.pr\0"
-"vaksdal.no\0edu.ps\0dni.us\0"
-"po.gov.pl\0edu.pt\0"
-"nordre-land.no\0vadso.no\0"
-"rnrt.tn\0"
-"sport.hu\0!pref.gifu.jp\0voss.no\0targi.pl\0"
-"flesberg.no\0"
-"photography.museum\0"
-"modena.it\0tonsberg.no\0"
-"ac.at\0"
-"ac.be\0coop.br\0"
-"services.aero\0"
-"nom.mg\0"
-"wielun.pl\0"
-"jefferson.museum\0wy.us\0"
-"pd.it\0ot.it\0neues.museum\0slattum.no\0"
-"vdonsk.ru\0"
-"ar.com\0edu.sa\0"
-"\xc3\xa5l.no\0edu.sb\0"
-"edu.rs\0edu.sc\0"
-"ac.ci\0int.is\0edu.sd\0!tsk.tr\0"
-"br\xc3\xb8nn\xc3\xb8ysund.no\0and\xc3\xb8y.no\0edu.ru\0"
-"pol.ht\0"
-"edu.rw\0edu.sg\0"
-"gyeongnam.kr\0olecko.pl\0"
-"ac.cn\0"
-"graz.museum\0"
-"coldwar.museum\0edu.sl\0"
-"ac.cr\0"
-"edu.sn\0"
-"hamar.no\0"
-"histoire.museum\0"
-"!city.shizuoka.jp\0"
-"edu.st\0"
-"oceanographic.museum\0nh.us\0"
-"x.bg\0"
-"surnadal.no\0"
-"fc.it\0costume.museum\0stalowa-wola.pl\0"
-"valer.ostfold.no\0edu.sy\0"
-"edu.tj\0"
-"arq.br\0"
-"aeroclub.aero\0odo.br\0pe.ca\0\xe7\xb6\xb2\xe7\xb5\xa1.cn\0bronnoysund.no\0nom.pa\0"
-"edu.to\0"
-"paleo.museum\0nom.pe\0edu.ua\0"
-"int.la\0trustee.museum\0forsand.no\0krasnoyarsk.ru\0"
-"!pref.hyogo.jp\0"
-"edu.tt\0"
-"zarow.pl\0"
-"edu.tw\0"
-"nom.pl\0"
-"community.museum\0kvitsoy.no\0"
-"int.lk\0tychy.pl\0"
-"k12.me.us\0"
-"jondal.no\0edu.vc\0"
-"illustration.museum\0"
-"clinton.museum\0"
-"tas.au\0es.kr\0"
-"production.aero\0"
-"rodoy.no\0"
-"database.museum\0bodo.no\0"
-"anthro.museum\0landes.museum\0edu.vn\0"
-"nom.re\0"
-"altai.ru\0"
-"filatelia.museum\0"
-"sk.ca\0lezajsk.pl\0"
-"rockart.museum\0int.mv\0"
-"int.mw\0herad.no\0"
-"eti.br\0ac.gn\0"
-"fedje.no\0nom.ro\0"
-"money.museum\0"
-"\xd9\x85\xd8\xb5\xd8\xb1\0"
-"horten.no\0"
-"gangaviika.no\0mielec.pl\0"
-"uw.gov.pl\0"
-"moma.museum\0"
-"edu.ws\0"
-"go.ci\0"
-"tv.bo\0technology.museum\0"
-"s\xc3\xb8ndre-land.no\0"
-"tv.br\0"
-"jor.br\0lib.dc.us\0"
-"arboretum.museum\0"
-"go.cr\0"
-"artsandcrafts.museum\0\xd8\xaa\xd9\x88\xd9\x86\xd8\xb3\0"
-"psc.br\0ac.id\0!city.chiba.jp\0"
-"wa.au\0"
-"rome.it\0"
-"amli.no\0"
-"ac.im\0lo.it\0"
-"ac.in\0"
-"\xe7\xb6\xb2\xe7\xb5\xa1.hk\0durham.museum\0"
-"ac.ir\0"
-"torino.museum\0"
-"loabat.no\0"
-"com\0"
-"nalchik.ru\0"
-"yakutia.ru\0"
-"settlers.museum\0"
-"!promocion.ar\0int.pt\0"
-"union.aero\0"
-"utah.museum\0"
-"giehtavuoatna.no\0"
-"ac.jp\0"
-"air-traffic-control.aero\0"
-"silk.museum\0usantiques.museum\0"
-"bn.it\0"
-"kalisz.pl\0"
-"perm.ru\0"
-"aoste.it\0bindal.no\0"
-"coloradoplateau.museum\0k12.gu.us\0"
-"frosinone.it\0forde.no\0"
-"epilepsy.museum\0"
-"olbia-tempio.it\0"
-"journalist.aero\0ac.kr\0*.sch.uk\0"
-"nic.im\0sciencesnaturelles.museum\0bedzin.pl\0"
-"nic.in\0pe.it\0"
-"w.se\0"
-"!pref.okayama.jp\0"
-"urn.arpa\0"
-"cinema.museum\0"
-"monza.it\0versailles.museum\0int.ru\0"
-"andasuolo.no\0skj\xc3\xa5k.no\0chernovtsy.ua\0"
-"nyc.museum\0int.rw\0paroch.k12.ma.us\0"
-"ringerike.no\0"
-"ac.ma\0"
-"org.ac\0civilaviation.aero\0"
-"rakkestad.no\0"
-"org.ae\0ac.me\0"
-"org.af\0"
-"org.ag\0"
-"org.ai\0stokke.no\0"
-"airport.aero\0"
-"finnoy.no\0"
-"org.al\0"
-"org.an\0y.bg\0habmer.no\0"
-"stadt.museum\0holtalen.no\0"
-"int.tj\0"
-"org.ba\0gjerdrum.no\0"
-"org.bb\0ascoli-piceno.it\0molde.no\0r\xc3\xb8st.no\0tysfjord.no\0"
-"pe.kr\0rybnik.pl\0"
-"go.id\0"
-"ac.mu\0"
-"ac.mw\0ac.ng\0"
-"org.bh\0\xc3\xa5mot.no\0rana.no\0"
-"org.bi\0"
-"org.az\0belgorod.ru\0int.tt\0"
-"ae.org\0"
-"group.aero\0posts-and-telecommunications.museum\0"
-"org.bm\0salerno.it\0"
-"etnedal.no\0"
-"org.bo\0*.hokkaido.jp\0donetsk.ua\0"
-"ostroda.pl\0"
-"org.br\0"
-"org.bs\0"
-"go.it\0h\xc3\xb8ylandet.no\0"
-"zgorzelec.pl\0"
-"org.bw\0"
-"org.ci\0"
-"org.bz\0vicenza.it\0resistance.museum\0"
-"missile.museum\0"
-"org.cn\0"
-"org.co\0assassination.museum\0"
-"go.jp\0"
-"tv.it\0austrheim.no\0ac.pa\0"
-"verbania.it\0"
-"palace.museum\0"
-"tmp.br\0int.vn\0"
-"org.cu\0"
-"paris.museum\0"
-"media.aero\0hokksund.no\0"
-"arts.museum\0gemological.museum\0hammerfest.no\0"
-"k12.ny.us\0"
-"org.dm\0hemsedal.no\0ringsaker.no\0sklep.pl\0"
-"h\xc3\xa5.no\0cc.nj.us\0"
-"rzeszow.pl\0"
-"go.kr\0gjesdal.no\0ac.pr\0"
-"org.ec\0"
-"org.ee\0"
-"media.museum\0"
-"terni.it\0touch.museum\0zakopane.pl\0"
-"journal.aero\0org.dz\0"
-"incheon.kr\0"
-"b\xc3\xa1hcavuotna.no\0"
-"leksvik.no\0ulvik.no\0"
-"plantation.museum\0"
-"org.es\0loyalist.museum\0"
-"gildesk\xc3\xa5l.no\0bytom.pl\0"
-"bo.nordland.no\0"
-"ambulance.aero\0iglesiascarbonia.it\0"
-"tw.cn\0\xe6\x96\xb0\xe5\x8a\xa0\xe5\x9d\xa1\0"
-"chocolate.museum\0"
-"pittsburgh.museum\0"
-"royrvik.no\0sor-odal.no\0ac.rs\0"
-"kaluga.ru\0"
-"org.ge\0erotica.hu\0ac.ru\0ac.se\0"
-"org.gg\0leangaviika.no\0ac.rw\0"
-"org.gh\0v\xc3\xa6r\xc3\xb8y.no\0"
-"org.gi\0"
-"jevnaker.no\0"
-"org.gn\0tv.na\0leikanger.no\0"
-"org.gp\0"
-"ask\xc3\xb8y.no\0"
-"org.gr\0wroc.pl\0"
-"ad.jp\0"
-"powiat.pl\0"
-"tj\xc3\xb8me.no\0"
-"coop.tt\0"
-"ac.th\0"
-"mragowo.pl\0ac.sz\0ac.tj\0"
-"org.hk\0bo.it\0"
-"philately.museum\0"
-"org.hn\0"
-"fet.no\0"
-"axis.museum\0mansions.museum\0"
-"wiki.br\0"
-"org.ht\0"
-"org.hu\0piacenza.it\0scotland.museum\0cpa.pro\0"
-"ac.ug\0"
-"coop.mv\0x.se\0"
-"coop.mw\0ac.tz\0"
-"bmd.br\0"
-"org.im\0ralingen.no\0"
-"org.in\0"
-"cz.it\0lib.ia.us\0"
-"org.iq\0"
-"org.ir\0"
-"org.is\0"
-"nl.ca\0"
-"org.je\0"
-"childrensgarden.museum\0"
-"kvits\xc3\xb8y.no\0go.pw\0"
-"sokndal.no\0"
-"ra.it\0grimstad.no\0"
-"denmark.museum\0"
-"ac.vn\0"
-"ecn.br\0org.jo\0"
-"bialystok.pl\0nj.us\0"
-"z.bg\0bilbao.museum\0stargard.pl\0nic.tj\0"
-"eisenbahn.museum\0"
-"fe.it\0bryne.no\0vrn.ru\0"
-"cc.wa.us\0"
-"sex.hu\0skierva.no\0"
-"org.kg\0"
-"org.ki\0"
-"org.km\0"
-"org.kn\0khakassia.ru\0"
-"org.kp\0"
-"org.la\0"
-"org.lb\0"
-"org.lc\0"
-"francaise.museum\0"
-"panama.museum\0"
-"rotorcraft.aero\0gateway.museum\0olkusz.pl\0"
-"org.ky\0czeladz.pl\0ryazan.ru\0"
-"org.kz\0"
-"org.lk\0dyr\xc3\xb8y.no\0"
-"raisa.no\0"
-"dlugoleka.pl\0"
-"org.ma\0"
-"org.lr\0prochowice.pl\0"
-"org.ls\0"
-"org.me\0sandoy.no\0s\xc3\xb8r-varanger.no\0"
-"org.lv\0"
-"org.mg\0"
-"tel\0go.th\0"
-"org.ly\0"
-"steam.museum\0go.tj\0"
-"org.mk\0pasadena.museum\0jessheim.no\0lib.mn.us\0"
-"org.ml\0"
-"software.aero\0"
-"org.mn\0"
-"org.mo\0"
-"*.fukui.jp\0decorativearts.museum\0"
-"spy.museum\0org.na\0jorpeland.no\0"
-"vads\xc3\xb8.no\0"
-"org.mu\0building.museum\0gausdal.no\0"
-"org.mv\0nannestad.no\0"
-"org.mw\0org.ng\0go.ug\0"
-"vr.it\0org.mx\0"
-"org.my\0"
-"go.tz\0"
-"oppdal.no\0"
-"uk.net\0"
-"coop.km\0"
-"*.kyoto.jp\0"
-"sarpsborg.no\0org.nr\0"
-"chernigov.ua\0"
-"ha.cn\0no.com\0"
-"space.museum\0"
-"org.pa\0"
-"*.ar\0"
-"usgarden.museum\0"
-"*.bd\0org.pe\0"
-"*.au\0org.pf\0um.gov.pl\0"
-"bio.br\0"
-"org.ph\0"
-"org.pk\0"
-"fr\xc3\xa6na.no\0org.pl\0"
-"nord-aurdal.no\0org.pn\0"
-"*.bn\0handson.museum\0agrinet.tn\0"
-"kviteseid.no\0"
-"rel.ht\0virtuel.museum\0atm.pl\0org.pr\0"
-"org.ps\0cherkassy.ua\0"
-"org.pt\0wa.us\0"
-"*.bt\0arendal.no\0magnitka.ru\0"
-"depot.museum\0porsangu.no\0"
-"laakesvuemie.no\0"
-"sor-fron.no\0"
-"heroy.more-og-romsdal.no\0"
-"*.ck\0"
-"!rakpetroleum.om\0"
-"kr\xc3\xb8""dsherad.no\0mail.pl\0"
-"mod.gi\0"
-"gs.nl.no\0"
-"mb.ca\0"
-"pavia.it\0"
-"civilisation.museum\0folldal.no\0"
-"suli.hu\0"
-"brumunddal.no\0"
-"*.cy\0"
-"pg.it\0troms\xc3\xb8.no\0"
-"sex.pl\0y.se\0"
-"org.ro\0"
-"*.do\0"
-"caserta.it\0org.sa\0"
-"za.com\0halloffame.museum\0org.sb\0lviv.ua\0"
-"mill.museum\0org.rs\0org.sc\0"
-"org.sd\0"
-"idv.hk\0!omanmobile.om\0org.ru\0org.se\0"
-"langev\xc3\xa5g.no\0r\xc3\xa5holt.no\0starostwo.gov.pl\0"
-"trani-andria-barletta.it\0org.sg\0"
-"*.eg\0hvaler.no\0"
-"*.ehime.jp\0"
-"gmina.pl\0"
-"bod\xc3\xb8.no\0org.sl\0"
-"edu\0org.sn\0"
-"org.so\0lib.wi.us\0"
-"kommune.no\0"
-"nome.pt\0"
-"*.er\0namdalseid.no\0k12.wa.us\0"
-"nm.cn\0org.st\0"
-"*.et\0d\xc3\xb8nna.no\0"
-"jewish.museum\0preservation.museum\0"
-"slupsk.pl\0org.sy\0"
-"art.br\0org.sz\0org.tj\0"
-"ntr.br\0*.fj\0ski.no\0"
-"*.fk\0rimini.it\0grajewo.pl\0"
-"loppa.no\0"
-"franziskaner.museum\0notteroy.no\0org.tn\0"
-"org.to\0"
-"nesoddtangen.no\0"
-"org.ua\0"
-"discovery.museum\0wloclawek.pl\0"
-"lakas.hu\0org.tt\0"
-"kurgan.ru\0"
-"baltimore.museum\0nkz.ru\0org.tw\0"
-"com.ac\0castle.museum\0"
-"*.fukuoka.jp\0sandefjord.no\0varggat.no\0"
-"com.af\0"
-"com.ag\0"
-"ato.br\0k12.nj.us\0"
-"com.ai\0"
-"city.hu\0oryol.ru\0"
-"com.al\0nl.no\0mielno.pl\0cc.ma.us\0"
-"org.vc\0"
-"com.an\0g12.br\0"
-"*.gt\0"
-"*.gu\0"
-"com.ba\0"
-"com.bb\0americanart.museum\0"
-"org.vi\0"
-"kunstsammlung.museum\0"
-"com.aw\0"
-"flight.aero\0com.bh\0lib.mo.us\0org.vn\0"
-"com.bi\0adygeya.ru\0"
-"com.az\0"
-"art.dz\0"
-"com.bm\0"
-"dr\xc3\xb8""bak.no\0"
-"com.bo\0isla.pr\0"
-"com.br\0"
-"com.bs\0ustka.pl\0kuban.ru\0"
-"press.aero\0"
-"vs.it\0"
-"meloy.no\0"
-"*.il\0ulm.museum\0"
-"com.by\0com.ci\0genoa.it\0"
-"com.bz\0sn.cn\0"
-"lib.or.us\0"
-"santafe.museum\0org.ws\0"
-};
-
-QT_END_NAMESPACE
-
-#endif // QNETWORKCOOKIEJARTLD_P_H
diff --git a/src/network/access/qnetworkcookiejartlds_p.h.INFO b/src/network/access/qnetworkcookiejartlds_p.h.INFO
deleted file mode 100644
index 57a8d0e..0000000
--- a/src/network/access/qnetworkcookiejartlds_p.h.INFO
+++ /dev/null
@@ -1,17 +0,0 @@
-The file qnetworkcookiejartlds_p.h is generated from the Public Suffix
-List (see [1] and [2]), by the program residing at
-util/network/cookiejar-generateTLDs in the Qt source tree.
-
-That program generates a character array and an index array from the
-list to provide fast lookups of elements within C++.
-
-Those arrays in qnetworkcookiejartlds_p.h are derived from the Public
-Suffix List ([2]), which was originally provided by
-Jo Hermans <jo.hermans@gmail.com>.
-
-The file qnetworkcookiejartlds_p.h was last generated Friday,
-November 19th 15:24 2010.
-
-----
-[1] list: http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1
-[2] homepage: http://publicsuffix.org/
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index 3b18fe8..1c515c2 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -50,13 +50,15 @@
#include <qdir.h>
#include <qdatetime.h>
#include <qdiriterator.h>
-#include <qcryptographichash.h>
#include <qurl.h>
-
+#include <qcryptographichash.h>
#include <qdebug.h>
-#define CACHE_PREFIX QLatin1String("cache_")
-#define CACHE_POSTFIX QLatin1String(".cache")
+#define CACHE_POSTFIX QLatin1String(".d")
+#define PREPARED_SLASH QLatin1String("prepared/")
+#define CACHE_VERSION 7
+#define DATA_DIR QLatin1String("data")
+
#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3)
#ifndef QT_NO_NETWORKDISKCACHE
@@ -153,6 +155,9 @@ void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
d->cacheDirectory = dir.absolutePath();
if (!d->cacheDirectory.endsWith(QLatin1Char('/')))
d->cacheDirectory += QLatin1Char('/');
+
+ d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/');
+ d->prepareLayout();
}
/*!
@@ -244,6 +249,26 @@ void QNetworkDiskCache::insert(QIODevice *device)
d->inserting.erase(it);
}
+
+/*!
+ Create subdirectories and other housekeeping on the filesystem.
+ Prevents too many files from being present in any single directory.
+*/
+void QNetworkDiskCachePrivate::prepareLayout()
+{
+ QDir helper;
+ helper.mkpath(cacheDirectory + PREPARED_SLASH);
+
+ //Create directory and subdirectories 0-F
+ helper.mkpath(dataDirectory);
+ for (uint i = 0; i < 16 ; i++) {
+ QString str = QString::number(i, 16);
+ QString subdir = dataDirectory + str;
+ helper.mkdir(subdir);
+ }
+}
+
+
void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
{
Q_Q(QNetworkDiskCache);
@@ -324,7 +349,7 @@ bool QNetworkDiskCachePrivate::removeFile(const QString &file)
return false;
QFileInfo info(file);
QString fileName = info.fileName();
- if (!fileName.endsWith(CACHE_POSTFIX) || !fileName.startsWith(CACHE_PREFIX))
+ if (!fileName.endsWith(CACHE_POSTFIX))
return false;
qint64 size = info.size();
if (QFile::remove(file)) {
@@ -404,7 +429,7 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
// ### verify that QFile uses the fd size and not the file name
qint64 size = file->size() - file->pos();
const uchar *p = 0;
-#ifndef Q_OS_WINCE
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_INTEGRITY)
p = file->map(file->pos(), size);
#endif
if (p) {
@@ -508,6 +533,9 @@ qint64 QNetworkDiskCache::expire()
return 0;
}
+ // close file handle to prevent "in use" error when QFile::remove() is called
+ d->lastItem.reset();
+
QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
@@ -517,7 +545,7 @@ qint64 QNetworkDiskCache::expire()
QString path = it.next();
QFileInfo info = it.fileInfo();
QString fileName = info.fileName();
- if (fileName.endsWith(CACHE_POSTFIX) && fileName.startsWith(CACHE_PREFIX)) {
+ if (fileName.endsWith(CACHE_POSTFIX)) {
cacheItems.insert(info.created(), path);
totalSize += info.size();
}
@@ -544,8 +572,6 @@ qint64 QNetworkDiskCache::expire()
<< "Kept:" << cacheItems.count() - removedFiles;
}
#endif
- if (removedFiles > 0)
- d->lastItem.reset();
return totalSize;
}
@@ -564,7 +590,10 @@ void QNetworkDiskCache::clear()
d->maximumCacheSize = size;
}
-QByteArray QNetworkDiskCachePrivate::generateId(const QUrl &url) const
+/*!
+ Given a URL, generates a unique enough filename (and subdirectory)
+ */
+QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
{
QUrl cleanUrl = url;
cleanUrl.setPassword(QString());
@@ -572,29 +601,32 @@ QByteArray QNetworkDiskCachePrivate::generateId(const QUrl &url) const
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(cleanUrl.toEncoded());
- return hash.result().toHex();
+ // convert sha1 to base36 form and return first 8 bytes for use as string
+ QByteArray id = QByteArray::number(*(qlonglong*)hash.result().data(), 36).left(8);
+ // generates <one-char subdir>/<8-char filname.d>
+ uint code = (uint)id.at(id.length()-1) % 16;
+ QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
+ + QLatin1String(id) + CACHE_POSTFIX;
+
+ return pathFragment;
}
QString QNetworkDiskCachePrivate::tmpCacheFileName() const
{
- QDir dir;
- dir.mkpath(cacheDirectory + QLatin1String("prepared/"));
- return cacheDirectory + QLatin1String("prepared/") + CACHE_PREFIX + QLatin1String("XXXXXX") + CACHE_POSTFIX;
+ //The subdirectory is presumed to be already read for use.
+ return cacheDirectory + PREPARED_SLASH + QLatin1String("XXXXXX") + CACHE_POSTFIX;
}
+/*!
+ Generates fully qualified path of cached resource from a URL.
+ */
QString QNetworkDiskCachePrivate::cacheFileName(const QUrl &url) const
{
if (!url.isValid())
return QString();
- QString directory = cacheDirectory + url.scheme() + QLatin1Char('/');
- if (!QFile::exists(directory)) {
- // ### make a static QDir function for this...
- QDir dir;
- dir.mkpath(directory);
- }
- QString fileName = CACHE_PREFIX + QLatin1String(generateId(url)) + CACHE_POSTFIX;
- return directory + fileName;
+ QString fullpath = dataDirectory + uniqueFileName(url);
+ return fullpath;
}
/*!
@@ -631,7 +663,7 @@ bool QCacheItem::canCompress() const
enum
{
CacheMagic = 0xe8,
- CurrentCacheVersion = 7
+ CurrentCacheVersion = CACHE_VERSION
};
void QCacheItem::writeHeader(QFile *device) const
@@ -682,6 +714,12 @@ bool QCacheItem::read(QFile *device, bool readData)
data.setData(qUncompress(dataBA));
data.open(QBuffer::ReadOnly);
}
+
+ // quick and dirty check if metadata's URL field and the file's name are in synch
+ QString expectedFilename = QNetworkDiskCachePrivate::uniqueFileName(metaData.url());
+ if (!device->fileName().endsWith(expectedFilename))
+ return false;
+
return metaData.isValid();
}
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index e3a7698..ceb300d 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -104,14 +104,17 @@ public:
, currentCacheSize(-1)
{}
- QByteArray generateId(const QUrl &url) const;
+ static QString uniqueFileName(const QUrl &url);
QString cacheFileName(const QUrl &url) const;
QString tmpCacheFileName() const;
bool removeFile(const QString &file);
void storeItem(QCacheItem *item);
+ void prepareLayout();
+ static quint32 crc32(const char *data, uint len);
mutable QCacheItem lastItem;
QString cacheDirectory;
+ QString dataDirectory;
qint64 maximumCacheSize;
qint64 currentCacheSize;
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index c74e92a..1ce7e8f 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -49,6 +49,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
: readBufferMaxSize(0),
operation(QNetworkAccessManager::UnknownOperation),
errorCode(QNetworkReply::NoError)
+ , isFinished(false)
{
// set the default attribute values
attributes.insert(QNetworkRequest::ConnectionEncryptedAttribute, false);
@@ -462,7 +463,7 @@ QNetworkReply::NetworkError QNetworkReply::error() const
*/
bool QNetworkReply::isFinished() const
{
- return d_func()->isFinished();
+ return d_func()->isFinished;
}
/*!
@@ -718,6 +719,21 @@ void QNetworkReply::setError(NetworkError errorCode, const QString &errorString)
}
/*!
+ \since 4.8
+ Sets the reply as \a finished.
+
+ After having this set the replies data must not change.
+
+ \sa isFinished()
+*/
+void QNetworkReply::setFinished(bool finished)
+{
+ Q_D(QNetworkReply);
+ d->isFinished = finished;
+}
+
+
+/*!
Sets the URL being processed to be \a url. Normally, the URL
matches that of the request that was posted, but for a variety of
reasons it can be different (for example, a file path being made
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index b02fd4b..f8352a2 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -163,6 +163,7 @@ protected:
void setOperation(QNetworkAccessManager::Operation operation);
void setRequest(const QNetworkRequest &request);
void setError(NetworkError errorCode, const QString &errorString);
+ void setFinished(bool);
void setUrl(const QUrl &url);
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index a761d41..687e652 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -71,12 +71,11 @@ public:
qint64 readBufferMaxSize;
QNetworkAccessManager::Operation operation;
QNetworkReply::NetworkError errorCode;
+ bool isFinished;
static inline void setManager(QNetworkReply *reply, QNetworkAccessManager *manager)
{ reply->d_func()->manager = manager; }
- virtual bool isFinished() const { return false; }
-
Q_DECLARE_PUBLIC(QNetworkReply)
};
diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
new file mode 100644
index 0000000..0cd10ce
--- /dev/null
+++ b/src/network/access/qnetworkreplydataimpl.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreplydataimpl_p.h"
+#include "private/qdataurl_p.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMetaObject>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkReplyDataImplPrivate::QNetworkReplyDataImplPrivate()
+ : QNetworkReplyPrivate()
+{
+}
+
+QNetworkReplyDataImplPrivate::~QNetworkReplyDataImplPrivate()
+{
+}
+
+QNetworkReplyDataImpl::~QNetworkReplyDataImpl()
+{
+}
+
+QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
+ : QNetworkReply(*new QNetworkReplyDataImplPrivate(), parent)
+{
+ Q_D(QNetworkReplyDataImpl);
+ setRequest(req);
+ setUrl(req.url());
+ setOperation(op);
+ setFinished(true);
+ QNetworkReply::open(QIODevice::ReadOnly);
+
+ QUrl url = req.url();
+
+ // FIXME qDecodeDataUrl should instead be rewritten to have the QByteArray
+ // and the mime type as an output parameter and return a bool instead
+ d->decodeDataUrlResult = qDecodeDataUrl(url);
+
+ if (! d->decodeDataUrlResult.first.isNull()) {
+ QString &mimeType = d->decodeDataUrlResult.first;
+ qint64 size = d->decodeDataUrlResult.second.size();
+ setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
+ setHeader(QNetworkRequest::ContentLengthHeader, size);
+ QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
+
+ d->decodedData.setBuffer(&d->decodeDataUrlResult.second);
+ d->decodedData.open(QIODevice::ReadOnly);
+
+ QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
+ Q_ARG(qint64,size), Q_ARG(qint64, size));
+ QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ } else {
+ // something wrong with this URI
+ const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
+ "Invalid URI: %1").arg(QString::fromLatin1(url.toEncoded()));
+ setError(QNetworkReply::ProtocolFailure, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolFailure));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ }
+}
+
+void QNetworkReplyDataImpl::close()
+{
+ QNetworkReply::close();
+}
+
+void QNetworkReplyDataImpl::abort()
+{
+ QNetworkReply::close();
+}
+
+qint64 QNetworkReplyDataImpl::bytesAvailable() const
+{
+ Q_D(const QNetworkReplyDataImpl);
+ return QNetworkReply::bytesAvailable() + d->decodedData.bytesAvailable();
+}
+
+bool QNetworkReplyDataImpl::isSequential () const
+{
+ return true;
+}
+
+qint64 QNetworkReplyDataImpl::size() const
+{
+ Q_D(const QNetworkReplyDataImpl);
+ return d->decodedData.size();
+}
+
+/*!
+ \internal
+*/
+qint64 QNetworkReplyDataImpl::readData(char *data, qint64 maxlen)
+{
+ Q_D(QNetworkReplyDataImpl);
+
+ // TODO idea:
+ // Instead of decoding the whole data into new memory, we could decode on demand.
+ // Note that this might be tricky to do.
+
+ return d->decodedData.read(data, maxlen);
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qnetworkreplydataimpl_p.cpp"
+
diff --git a/src/network/access/qnetworkaccessdatabackend_p.h b/src/network/access/qnetworkreplydataimpl_p.h
index 57dfdef..a63c4b1 100644
--- a/src/network/access/qnetworkaccessdatabackend_p.h
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef QNETWORKACCESSDATABACKEND_P_H
-#define QNETWORKACCESSDATABACKEND_P_H
+#ifndef QNETWORKREPLYDATAIMPL_H
+#define QNETWORKREPLYDATAIMPL_H
//
// W A R N I N G
@@ -53,32 +53,46 @@
// We mean it.
//
-#include "qnetworkaccessbackend_p.h"
+#include "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkaccessmanager.h"
+#include <QBuffer>
QT_BEGIN_NAMESPACE
-class QNetworkAccessDataBackend: public QNetworkAccessBackend
+
+class QNetworkReplyDataImplPrivate;
+class QNetworkReplyDataImpl: public QNetworkReply
{
+ Q_OBJECT
public:
- QNetworkAccessDataBackend();
- virtual ~QNetworkAccessDataBackend();
+ QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
+ ~QNetworkReplyDataImpl();
+ virtual void abort();
+
+ // reimplemented from QNetworkReply
+ virtual void close();
+ virtual qint64 bytesAvailable() const;
+ virtual bool isSequential () const;
+ qint64 size() const;
- virtual void open();
- virtual void closeDownstreamChannel();
- virtual void closeUpstreamChannel();
- virtual bool waitForDownstreamReadyRead(int msecs);
- virtual bool waitForUpstreamBytesWritten(int msecs);
+ virtual qint64 readData(char *data, qint64 maxlen);
- virtual bool processRequestSynchronously();
+ Q_DECLARE_PRIVATE(QNetworkReplyDataImpl)
};
-class QNetworkAccessDataBackendFactory: public QNetworkAccessBackendFactory
+class QNetworkReplyDataImplPrivate: public QNetworkReplyPrivate
{
public:
- virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
- const QNetworkRequest &request) const;
+ QNetworkReplyDataImplPrivate();
+ ~QNetworkReplyDataImplPrivate();
+
+ QPair<QString, QByteArray> decodeDataUrlResult;
+ QBuffer decodedData;
+
+ Q_DECLARE_PUBLIC(QNetworkReplyDataImpl)
};
QT_END_NAMESPACE
-#endif
+#endif // QNETWORKREPLYDATAIMPL_H
diff --git a/src/network/access/qfilenetworkreply.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index 37e3578..7a0c5c7 100644
--- a/src/network/access/qfilenetworkreply.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -39,7 +39,7 @@
**
****************************************************************************/
-#include "qfilenetworkreply_p.h"
+#include "qnetworkreplyfileimpl_p.h"
#include "QtCore/qdatetime.h"
#include <QtCore/QCoreApplication>
@@ -48,31 +48,25 @@
QT_BEGIN_NAMESPACE
-QFileNetworkReplyPrivate::QFileNetworkReplyPrivate()
- : QNetworkReplyPrivate(), fileEngine(0), fileSize(0), filePos(0)
+QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
+ : QNetworkReplyPrivate(), realFileSize(0)
{
}
-QFileNetworkReplyPrivate::~QFileNetworkReplyPrivate()
+QNetworkReplyFileImpl::~QNetworkReplyFileImpl()
{
- delete fileEngine;
}
-QFileNetworkReply::~QFileNetworkReply()
-{
-}
-
-QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
- : QNetworkReply(*new QFileNetworkReplyPrivate(), parent)
+QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
+ : QNetworkReply(*new QNetworkReplyFileImplPrivate(), parent)
{
setRequest(req);
setUrl(req.url());
setOperation(op);
+ setFinished(true);
QNetworkReply::open(QIODevice::ReadOnly);
- qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
-
- QFileNetworkReplyPrivate *d = (QFileNetworkReplyPrivate*) d_func();
+ QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
QUrl url = req.url();
if (url.host() == QLatin1String("localhost"))
@@ -113,15 +107,15 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req
return;
}
- d->fileEngine = QAbstractFileEngine::create(fileName);
- bool opened = d->fileEngine->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+ d->realFile.setFileName(fileName);
+ bool opened = d->realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
// could we open the file?
if (!opened) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
- .arg(fileName, d->fileEngine->errorString());
+ .arg(d->realFile.fileName(), d->realFile.errorString());
- if (fi.exists()) {
+ if (d->realFile.exists()) {
setError(QNetworkReply::ContentAccessDenied, msg);
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
@@ -134,79 +128,62 @@ QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req
return;
}
- d->fileSize = fi.size();
setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
- setHeader(QNetworkRequest::ContentLengthHeader, d->fileSize);
+ d->realFileSize = fi.size();
+ setHeader(QNetworkRequest::ContentLengthHeader, d->realFileSize);
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
- Q_ARG(qint64, d->fileSize), Q_ARG(qint64, d->fileSize));
+ Q_ARG(qint64, d->realFileSize), Q_ARG(qint64, d->realFileSize));
QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
-
-bool QFileNetworkReplyPrivate::isFinished() const
+void QNetworkReplyFileImpl::close()
{
- return true;
-}
-
-void QFileNetworkReply::close()
-{
- Q_D(QFileNetworkReply);
+ Q_D(QNetworkReplyFileImpl);
QNetworkReply::close();
- if (d->fileEngine)
- d->fileEngine->close();
+ d->realFile.close();
}
-void QFileNetworkReply::abort()
+void QNetworkReplyFileImpl::abort()
{
- Q_D(QFileNetworkReply);
+ Q_D(QNetworkReplyFileImpl);
QNetworkReply::close();
- if (d->fileEngine)
- d->fileEngine->close();
+ d->realFile.close();
}
-qint64 QFileNetworkReply::bytesAvailable() const
+qint64 QNetworkReplyFileImpl::bytesAvailable() const
{
- Q_D(const QFileNetworkReply);
- if (!d->fileEngine)
- return 0;
-
- return QNetworkReply::bytesAvailable() + d->fileSize - d->filePos;
+ Q_D(const QNetworkReplyFileImpl);
+ return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable();
}
-bool QFileNetworkReply::isSequential () const
+bool QNetworkReplyFileImpl::isSequential () const
{
return true;
}
-qint64 QFileNetworkReply::size() const
+qint64 QNetworkReplyFileImpl::size() const
{
- Q_D(const QFileNetworkReply);
- return d->fileSize;
+ Q_D(const QNetworkReplyFileImpl);
+ return d->realFileSize;
}
/*!
\internal
*/
-qint64 QFileNetworkReply::readData(char *data, qint64 maxlen)
+qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
{
- Q_D(QFileNetworkReply);
- if (!d->fileEngine)
+ Q_D(QNetworkReplyFileImpl);
+ qint64 ret = d->realFile.read(data, maxlen);
+ if (ret == 0 && bytesAvailable() == 0)
return -1;
-
- qint64 ret = d->fileEngine->read(data, maxlen);
- if (ret == 0 && bytesAvailable() == 0) {
- return -1; // everything had been read
- } else if (ret > 0) {
- d->filePos += ret;
- }
-
- return ret;
+ else
+ return ret;
}
QT_END_NAMESPACE
-#include "moc_qfilenetworkreply_p.cpp"
+#include "moc_qnetworkreplyfileimpl_p.cpp"
diff --git a/src/network/access/qfilenetworkreply_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index ec8aa4e..c9f5def 100644
--- a/src/network/access/qfilenetworkreply_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef QFILENETWORKREPLY_P_H
-#define QFILENETWORKREPLY_P_H
+#ifndef QNETWORKREPLYFILEIMPL_H
+#define QNETWORKREPLYFILEIMPL_H
//
// W A R N I N G
@@ -62,13 +62,13 @@
QT_BEGIN_NAMESPACE
-class QFileNetworkReplyPrivate;
-class QFileNetworkReply: public QNetworkReply
+class QNetworkReplyFileImplPrivate;
+class QNetworkReplyFileImpl: public QNetworkReply
{
Q_OBJECT
public:
- QFileNetworkReply(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
- ~QFileNetworkReply();
+ QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
+ ~QNetworkReplyFileImpl();
virtual void abort();
// reimplemented from QNetworkReply
@@ -79,24 +79,20 @@ public:
virtual qint64 readData(char *data, qint64 maxlen);
- Q_DECLARE_PRIVATE(QFileNetworkReply)
+ Q_DECLARE_PRIVATE(QNetworkReplyFileImpl)
};
-class QFileNetworkReplyPrivate: public QNetworkReplyPrivate
+class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate
{
public:
- QFileNetworkReplyPrivate();
- ~QFileNetworkReplyPrivate();
+ QNetworkReplyFileImplPrivate();
- QAbstractFileEngine *fileEngine;
- qint64 fileSize;
- qint64 filePos;
+ QFile realFile;
+ qint64 realFileSize;
- virtual bool isFinished() const;
-
- Q_DECLARE_PUBLIC(QFileNetworkReply)
+ Q_DECLARE_PUBLIC(QNetworkReplyFileImpl)
};
QT_END_NAMESPACE
-#endif // QFILENETWORKREPLY_P_H
+#endif // QNETWORKREPLYFILEIMPL_H
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index a7a6287..0b568d4 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -52,23 +52,29 @@
#include <QtCore/QCoreApplication>
+Q_DECLARE_METATYPE(QSharedPointer<char>)
+
QT_BEGIN_NAMESPACE
inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
- : backend(0), outgoingData(0), outgoingDataBuffer(0),
+ : backend(0), outgoingData(0),
copyDevice(0),
cacheEnabled(false), cacheSaveDevice(0),
notificationHandlingPaused(false),
bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
httpStatusCode(0),
state(Idle)
+ , downloadBufferReadPosition(0)
+ , downloadBufferCurrentSize(0)
+ , downloadBufferMaximumSize(0)
+ , downloadBuffer(0)
{
}
void QNetworkReplyImplPrivate::_q_startOperation()
{
// ensure this function is only being called once
- if (state == Working) {
+ if (state == Working || state == Finished) {
qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
return;
}
@@ -84,10 +90,10 @@ void QNetworkReplyImplPrivate::_q_startOperation()
return;
}
+ if (!backend->start()) {
#ifndef QT_NO_BEARERMANAGEMENT
- if (!backend->start()) { // ### we should call that method even if bearer is not used
// backend failed to start because the session state is not Connected.
- // QNetworkAccessManager will call reply->backend->start() again for us when the session
+ // QNetworkAccessManager will call _q_startOperation again for us when the session
// state changes.
state = WaitingForSession;
@@ -103,14 +109,24 @@ void QNetworkReplyImplPrivate::_q_startOperation()
session->open();
} else {
qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
+ state = Working;
+ error(QNetworkReplyImpl::UnknownNetworkError,
+ QCoreApplication::translate("QNetworkReply", "Network session error."));
+ finished();
}
-
+#else
+ qWarning("Backend start failed");
+ state = Working;
+ error(QNetworkReplyImpl::UnknownNetworkError,
+ QCoreApplication::translate("QNetworkReply", "backend start error."));
+ finished();
+#endif
return;
}
-#endif
if (backend && backend->isSynchronous()) {
state = Finished;
+ q_func()->setFinished(true);
} else {
if (state != Finished) {
if (operation == QNetworkAccessManager::GetOperation)
@@ -129,6 +145,10 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
if (!copyDevice || !q->isOpen())
return;
+ // FIXME Optimize to use download buffer if it is a QBuffer.
+ // Needs to be done where sendCacheContents() (?) of HTTP is emitting
+ // metaDataChanged ?
+
forever {
qint64 bytesToRead = nextDownstreamBlockSize();
if (bytesToRead == 0)
@@ -167,9 +187,11 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
if (preMigrationDownloaded != Q_INT64_C(-1))
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
pauseNotificationHandling();
+ // emit readyRead before downloadProgress incase this will cause events to be
+ // processed and we get into a recursive call (as in QProgressDialog).
+ emit q->readyRead();
emit q->downloadProgress(bytesDownloaded,
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
- emit q->readyRead();
resumeNotificationHandling();
}
@@ -201,7 +223,7 @@ void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
if (!outgoingDataBuffer) {
// first call, create our buffer
- outgoingDataBuffer = new QRingBuffer();
+ outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
@@ -300,20 +322,22 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
// Internal code that does a HTTP reply for the synchronous Ajax
// in QtWebKit.
QVariant synchronousHttpAttribute = req.attribute(
- static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1));
- if (backend && synchronousHttpAttribute.toBool()) {
- backend->setSynchronous(true);
- if (outgoingData && outgoingData->isSequential()) {
- outgoingDataBuffer = new QRingBuffer();
- QByteArray data;
- do {
- data = outgoingData->readAll();
- if (data.isEmpty())
- break;
- outgoingDataBuffer->append(data);
- } while (1);
- }
+ static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
+ // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
+ // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
+ if (synchronousHttpAttribute.toBool() && outgoingData) {
+ outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
+ qint64 previousDataSize = 0;
+ do {
+ previousDataSize = outgoingDataBuffer->size();
+ outgoingDataBuffer->append(outgoingData->readAll());
+ } while (outgoingDataBuffer->size() != previousDataSize);
}
+
+ if (backend)
+ backend->setSynchronous(synchronousHttpAttribute.toBool());
+
+
if (outgoingData && backend && !backend->isSynchronous()) {
// there is data to be uploaded, e.g. HTTP POST.
@@ -343,10 +367,6 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
}
}
} else {
- // No outgoing data (e.g. HTTP GET request)
- // or no backend
- // if no backend, _q_startOperation will handle the error of this
-
// 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
@@ -578,25 +598,22 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
{
Q_Q(QNetworkReplyImpl);
- 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());
// important: At the point of this readyRead(), the data parameter list must be empty,
// else implicit sharing will trigger memcpy when the user is reading data!
emit q->readyRead();
+ // emit readyRead before downloadProgress incase this will cause events to be
+ // processed and we get into a recursive call (as in QProgressDialog).
+ emit q->downloadProgress(bytesDownloaded,
+ totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
- // hopefully we haven't been deleted here
- if (!qq.isNull()) {
- resumeNotificationHandling();
- // do we still have room in the buffer?
- if (nextDownstreamBlockSize() > 0)
- backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
- }
+ resumeNotificationHandling();
+ // do we still have room in the buffer?
+ if (nextDownstreamBlockSize() > 0)
+ backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
}
// this is used when it was fetched from the cache, right?
@@ -631,6 +648,75 @@ void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
}
+static void downloadBufferDeleter(char *ptr)
+{
+ delete[] ptr;
+}
+
+char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
+{
+ Q_Q(QNetworkReplyImpl);
+
+ if (!downloadBuffer) {
+ // We are requested to create it
+ // Check attribute() if allocating a buffer of that size can be allowed
+ QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
+ if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
+ downloadBufferCurrentSize = 0;
+ downloadBufferMaximumSize = size;
+ downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
+ downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
+
+ q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+ }
+ }
+
+ return downloadBuffer;
+}
+
+void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
+{
+ Q_Q(QNetworkReplyImpl);
+
+ downloadBufferPointer = sp;
+ downloadBuffer = downloadBufferPointer.data();
+ downloadBufferCurrentSize = 0;
+ downloadBufferMaximumSize = size;
+ q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+}
+
+
+void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!q->isOpen())
+ return;
+
+ if (cacheEnabled && !cacheSaveDevice)
+ initCacheSaveDevice();
+
+ if (cacheSaveDevice && bytesReceived == bytesTotal) {
+// if (lastBytesDownloaded == -1)
+// lastBytesDownloaded = 0;
+// cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
+
+ // Write everything in one go if we use a download buffer. might be more performant.
+ cacheSaveDevice->write(downloadBuffer, bytesTotal);
+ }
+
+ bytesDownloaded = bytesReceived;
+ lastBytesDownloaded = bytesReceived;
+
+ downloadBufferCurrentSize = bytesReceived;
+
+ // Only emit readyRead when actual data is there
+ // emit readyRead before downloadProgress incase this will cause events to be
+ // processed and we get into a recursive call (as in QProgressDialog).
+ if (bytesDownloaded > 0)
+ emit q->readyRead();
+ emit q->downloadProgress(bytesDownloaded, bytesTotal);
+}
+
void QNetworkReplyImplPrivate::finished()
{
Q_Q(QNetworkReplyImpl);
@@ -669,6 +755,8 @@ void QNetworkReplyImplPrivate::finished()
resumeNotificationHandling();
state = Finished;
+ q->setFinished(true);
+
pendingNotifications.clear();
pauseNotificationHandling();
@@ -696,6 +784,11 @@ void QNetworkReplyImplPrivate::finished()
void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
{
Q_Q(QNetworkReplyImpl);
+ // Can't set and emit multiple errors.
+ if (errorCode != QNetworkReply::NoError) {
+ qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.";
+ return;
+ }
errorCode = code;
q->setErrorString(errorMessage);
@@ -739,11 +832,6 @@ void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
#endif
}
-bool QNetworkReplyImplPrivate::isFinished() const
-{
- return (state == Finished || state == Aborted);
-}
-
QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
: QNetworkReply(*new QNetworkReplyImplPrivate, parent)
{
@@ -758,9 +846,6 @@ QNetworkReplyImpl::~QNetworkReplyImpl()
// save had been properly finished. So if it is still enabled it means we got deleted/aborted.
if (d->isCachingEnabled())
d->networkCache()->remove(url());
-
- if (d->outgoingDataBuffer)
- delete d->outgoingDataBuffer;
}
void QNetworkReplyImpl::abort()
@@ -778,7 +863,7 @@ void QNetworkReplyImpl::abort()
QNetworkReply::close();
if (d->state != QNetworkReplyImplPrivate::Finished) {
- // emit signals
+ // call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
d->state = QNetworkReplyImplPrivate::Working;
@@ -808,7 +893,7 @@ void QNetworkReplyImpl::close()
QNetworkReply::close();
- // emit signals
+ // call finished which will emit signals
d->error(OperationCanceledError, tr("Operation canceled"));
d->finished();
}
@@ -827,6 +912,13 @@ bool QNetworkReplyImpl::canReadLine () const
*/
qint64 QNetworkReplyImpl::bytesAvailable() const
{
+ // Special case for the "zero copy" download buffer
+ Q_D(const QNetworkReplyImpl);
+ if (d->downloadBuffer) {
+ qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
+ return QNetworkReply::bytesAvailable() + maxAvail;
+ }
+
return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
}
@@ -881,8 +973,22 @@ void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &er
qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
{
Q_D(QNetworkReplyImpl);
+
+ // Special case code if we have the "zero copy" download buffer
+ if (d->downloadBuffer) {
+ qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
+ if (maxAvail == 0)
+ return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
+ // FIXME what about "Aborted" state?
+ qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
+ d->downloadBufferReadPosition += maxAvail;
+ return maxAvail;
+ }
+
+
if (d->readBuffer.isEmpty())
return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
+ // FIXME what about "Aborted" state?
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
if (maxlen == 1) {
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index 31da297..089c87e 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -62,6 +62,7 @@
#include "QtCore/qbuffer.h"
#include "private/qringbuffer_p.h"
#include "private/qbytedata_p.h"
+#include <QSharedPointer>
QT_BEGIN_NAMESPACE
@@ -163,17 +164,19 @@ public:
void appendDownstreamData(QIODevice *data);
void appendDownstreamData(const QByteArray &data);
+ void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
+ char* getDownloadBuffer(qint64 size);
+ void appendDownstreamDataDownloadBuffer(qint64, qint64);
+
void finished();
void error(QNetworkReply::NetworkError code, const QString &errorString);
void metaDataChanged();
void redirectionRequested(const QUrl &target);
void sslErrors(const QList<QSslError> &errors);
- bool isFinished() const;
-
QNetworkAccessBackend *backend;
QIODevice *outgoingData;
- QRingBuffer *outgoingDataBuffer;
+ QSharedPointer<QRingBuffer> outgoingDataBuffer;
QIODevice *copyDevice;
QAbstractNetworkCache *networkCache() const;
@@ -191,6 +194,7 @@ public:
QList<QNetworkProxy> proxyList;
#endif
+ // Used for normal downloading. For "zero copy" the downloadBuffer is used
QByteDataBuffer readBuffer;
qint64 bytesDownloaded;
qint64 lastBytesDownloaded;
@@ -202,6 +206,14 @@ public:
State state;
+ // only used when the "zero copy" style is used. Else readBuffer is used.
+ // Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
+ qint64 downloadBufferReadPosition;
+ qint64 downloadBufferCurrentSize;
+ qint64 downloadBufferMaximumSize;
+ QSharedPointer<char> downloadBufferPointer;
+ char* downloadBuffer;
+
Q_DECLARE_PUBLIC(QNetworkReplyImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index cad8ef6..8b3b359 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -226,6 +226,8 @@ QT_BEGIN_NAMESPACE
\omitvalue DownloadBufferAttribute
+ \omitvalue SynchronousRequestAttribute
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -522,7 +524,7 @@ void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
QSslConfiguration QNetworkRequest::sslConfiguration() const
{
if (!d->sslConfiguration)
- d->sslConfiguration = new QSslConfiguration;
+ d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
return *d->sslConfiguration;
}
@@ -637,6 +639,9 @@ static QByteArray headerName(QNetworkRequest::KnownHeaders header)
case QNetworkRequest::SetCookieHeader:
return "Set-Cookie";
+ case QNetworkRequest::ContentDispositionHeader:
+ return "Content-Disposition";
+
// no default:
// if new values are added, this will generate a compiler warning
}
@@ -649,6 +654,7 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
switch (header) {
case QNetworkRequest::ContentTypeHeader:
case QNetworkRequest::ContentLengthHeader:
+ case QNetworkRequest::ContentDispositionHeader:
return value.toByteArray();
case QNetworkRequest::LocationHeader:
@@ -757,7 +763,7 @@ static QVariant parseCookieHeader(const QByteArray &raw)
result += parsed;
}
- return qVariantFromValue(result);
+ return QVariant::fromValue(result);
}
static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
@@ -790,7 +796,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
return parseCookieHeader(value);
case QNetworkRequest::SetCookieHeader:
- return qVariantFromValue(QNetworkCookie::parseCookies(value));
+ return QVariant::fromValue(QNetworkCookie::parseCookies(value));
default:
Q_ASSERT(0);
@@ -810,6 +816,11 @@ QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
return end; // not found
}
+QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
+{
+ return rawHeaders;
+}
+
QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
{
QList<QByteArray> result;
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 6d0e02c..eedb0f6 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -65,7 +65,8 @@ public:
LocationHeader,
LastModifiedHeader,
CookieHeader,
- SetCookieHeader
+ SetCookieHeader,
+ ContentDispositionHeader // added for QMultipartMessage
};
enum Attribute {
HttpStatusCodeAttribute,
@@ -84,9 +85,7 @@ public:
CookieSaveControlAttribute,
MaximumDownloadBufferSizeAttribute, // internal
DownloadBufferAttribute, // internal
-
- // (DownloadBufferAttribute + 1) is reserved internal for QSynchronousHttpNetworkReply
- // add the enum in 4.8
+ SynchronousRequestAttribute, // internal
User = 1000,
UserMax = 32767
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 80b4bdc..54251c8 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -62,7 +62,7 @@
QT_BEGIN_NAMESPACE
-// this is the common part between QNetworkRequestPrivate and QNetworkReplyPrivate
+// this is the common part between QNetworkRequestPrivate, QNetworkReplyPrivate and QHttpPartPrivate
class QNetworkHeadersPrivate
{
public:
@@ -77,6 +77,7 @@ public:
QWeakPointer<QObject> originatingObject;
RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
+ RawHeadersList allRawHeaders() const;
QList<QByteArray> rawHeadersKeys() const;
void setRawHeader(const QByteArray &key, const QByteArray &value);
void setAllRawHeaders(const RawHeadersList &list);
diff --git a/src/network/bearer/bearer.pri b/src/network/bearer/bearer.pri
index 684e02b..d58d5ec 100644
--- a/src/network/bearer/bearer.pri
+++ b/src/network/bearer/bearer.pri
@@ -17,4 +17,3 @@ SOURCES += bearer/qnetworksession.cpp \
bearer/qbearerengine.cpp \
bearer/qbearerplugin.cpp \
bearer/qsharednetworksession.cpp
-
diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp
index f9570fd..4dc2145 100644
--- a/src/network/bearer/qbearerengine.cpp
+++ b/src/network/bearer/qbearerengine.cpp
@@ -46,7 +46,7 @@
QT_BEGIN_NAMESPACE
QBearerEngine::QBearerEngine(QObject *parent)
-: QObject(parent), mutex(QMutex::Recursive)
+ : QObject(parent), mutex(QMutex::Recursive)
{
}
@@ -54,6 +54,7 @@ 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();
@@ -93,19 +94,20 @@ bool QBearerEngine::configurationsInUse() const
QMutexLocker locker(&mutex);
- for (it = accessPointConfigurations.begin(),
- end = accessPointConfigurations.end(); it != end; ++it) {
+ for (it = accessPointConfigurations.constBegin(),
+ end = accessPointConfigurations.constEnd(); it != end; ++it) {
if (it.value()->ref > 1)
return true;
}
- for (it = snapConfigurations.begin(), end = snapConfigurations.end(); it != end; ++it) {
+ for (it = snapConfigurations.constBegin(),
+ end = snapConfigurations.constEnd(); it != end; ++it) {
if (it.value()->ref > 1)
return true;
}
- for (it = userChoiceConfigurations.begin(),
- end = userChoiceConfigurations.end(); it != end; ++it) {
+ for (it = userChoiceConfigurations.constBegin(),
+ end = userChoiceConfigurations.constEnd(); it != end; ++it) {
if (it.value()->ref > 1)
return true;
}
diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h
index 17a24ae..64af68a 100644
--- a/src/network/bearer/qbearerengine_p.h
+++ b/src/network/bearer/qbearerengine_p.h
@@ -78,7 +78,7 @@ class Q_NETWORK_EXPORT QBearerEngine : public QObject
friend class QNetworkConfigurationManagerPrivate;
public:
- QBearerEngine(QObject *parent = 0);
+ explicit QBearerEngine(QObject *parent = 0);
virtual ~QBearerEngine();
virtual bool hasIdentifier(const QString &id) = 0;
@@ -96,7 +96,6 @@ Q_SIGNALS:
void configurationAdded(QNetworkConfigurationPrivatePointer config);
void configurationRemoved(QNetworkConfigurationPrivatePointer config);
void configurationChanged(QNetworkConfigurationPrivatePointer config);
-
void updateCompleted();
protected:
@@ -114,4 +113,4 @@ QT_END_NAMESPACE
#endif // QT_NO_BEARERMANAGEMENT
-#endif
+#endif // QBEARERENGINE_P_H
diff --git a/src/network/bearer/qbearerplugin.cpp b/src/network/bearer/qbearerplugin.cpp
index 21a3555..bea7116 100644
--- a/src/network/bearer/qbearerplugin.cpp
+++ b/src/network/bearer/qbearerplugin.cpp
@@ -41,14 +41,12 @@
#include "qbearerplugin_p.h"
-#include <QtCore/qdebug.h>
-
#ifndef QT_NO_BEARERMANAGEMENT
QT_BEGIN_NAMESPACE
QBearerEnginePlugin::QBearerEnginePlugin(QObject *parent)
-: QObject(parent)
+ : QObject(parent)
{
}
diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h
index 4daa737..424dfe1 100644
--- a/src/network/bearer/qbearerplugin_p.h
+++ b/src/network/bearer/qbearerplugin_p.h
@@ -68,7 +68,7 @@ QT_MODULE(Network)
struct Q_NETWORK_EXPORT QBearerEngineFactoryInterface : public QFactoryInterface
{
- virtual QBearerEngine *create(const QString &key = QString()) const = 0;
+ virtual QBearerEngine *create(const QString &key) const = 0;
};
#define QBearerEngineFactoryInterface_iid "com.trolltech.Qt.QBearerEngineFactoryInterface"
@@ -84,7 +84,7 @@ public:
virtual ~QBearerEnginePlugin();
virtual QStringList keys() const = 0;
- virtual QBearerEngine *create(const QString &key = QString()) const = 0;
+ virtual QBearerEngine *create(const QString &key) const = 0;
};
QT_END_NAMESPACE
@@ -93,4 +93,4 @@ QT_END_HEADER
#endif // QT_NO_BEARERMANAGEMENT
-#endif
+#endif // QBEARERPLUGIN_P_H
diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp
index d2c7317..8065025 100644
--- a/src/network/bearer/qnetworkconfigmanager.cpp
+++ b/src/network/bearer/qnetworkconfigmanager.cpp
@@ -52,7 +52,8 @@
QT_BEGIN_NAMESPACE
#define Q_GLOBAL_STATIC_QAPP_DESTRUCTION(TYPE, NAME) \
- Q_GLOBAL_STATIC_INIT(TYPE, NAME); \
+ static QGlobalStatic<TYPE > this_##NAME \
+ = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \
static void NAME##_cleanup() \
{ \
this_##NAME.pointer->cleanup(); \
@@ -117,17 +118,18 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
*/
/*!
- \fn void QNetworkConfigurationManager::configurationAdded(const QNetworkConfiguration& config)
+ \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)
+ \fn void QNetworkConfigurationManager::configurationRemoved(const QNetworkConfiguration &config)
+ \since 4.8
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.
+ configuration, specified by \a config, is invalid but retains name and identifier.
*/
/*!
@@ -137,7 +139,7 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
be initiated via \l updateConfigurations().
*/
-/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration& config)
+/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration &config)
This signal is emitted when the \l {QNetworkConfiguration::state()}{state} of \a config changes.
*/
@@ -200,7 +202,7 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
Note that to ensure a valid list of current configurations immediately available, updating
is done during construction which causes some delay.
*/
-QNetworkConfigurationManager::QNetworkConfigurationManager( QObject* parent )
+QNetworkConfigurationManager::QNetworkConfigurationManager(QObject *parent)
: QObject(parent)
{
QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
@@ -209,12 +211,12 @@ QNetworkConfigurationManager::QNetworkConfigurationManager( QObject* parent )
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)));
+ connect(priv, SIGNAL(onlineStateChanged(bool)),
+ this, SIGNAL(onlineStateChanged(bool)));
+ connect(priv, SIGNAL(configurationUpdateComplete()),
+ this, SIGNAL(updateCompleted()));
priv->enablePolling();
}
diff --git a/src/network/bearer/qnetworkconfigmanager.h b/src/network/bearer/qnetworkconfigmanager.h
index 325f2df..0323892 100644
--- a/src/network/bearer/qnetworkconfigmanager.h
+++ b/src/network/bearer/qnetworkconfigmanager.h
@@ -68,7 +68,6 @@ class QNetworkConfigurationManagerExport QNetworkConfigurationManager : public Q
Q_OBJECT
public:
-
enum Capability {
CanStartAndStopInterfaces = 0x00000001,
DirectConnectionRouting = 0x00000002,
@@ -81,26 +80,26 @@ public:
Q_DECLARE_FLAGS(Capabilities, Capability)
- QNetworkConfigurationManager( QObject* parent = 0 );
+ explicit QNetworkConfigurationManager(QObject *parent = 0);
virtual ~QNetworkConfigurationManager();
-
QNetworkConfigurationManager::Capabilities capabilities() const;
- QNetworkConfiguration defaultConfiguration() const;
+ QNetworkConfiguration defaultConfiguration() const;
QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags flags = 0) const;
- QNetworkConfiguration configurationFromIdentifier(const QString& identifier) const;
- void updateConfigurations();
+ QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const;
bool isOnline() const;
+public Q_SLOTS:
+ void updateConfigurations();
+
Q_SIGNALS:
- void configurationAdded(const QNetworkConfiguration& config);
- void configurationRemoved(const QNetworkConfiguration& config);
- void configurationChanged(const QNetworkConfiguration& config);
+ 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)
@@ -115,5 +114,4 @@ QT_END_HEADER
#endif // QT_NO_BEARERMANAGEMENT
-#endif //QNETWORKCONFIGURATIONMANAGER_H
-
+#endif // QNETWORKCONFIGURATIONMANAGER_H
diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp
index 17e47bd..18e29af 100644
--- a/src/network/bearer/qnetworkconfigmanager_p.cpp
+++ b/src/network/bearer/qnetworkconfigmanager_p.cpp
@@ -60,7 +60,7 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
#endif
QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate()
-: pollTimer(0), mutex(QMutex::Recursive), forcedPolling(0), firstUpdate(true)
+ : QObject(), pollTimer(0), mutex(QMutex::Recursive), forcedPolling(0), firstUpdate(true)
{
qRegisterMetaType<QNetworkConfiguration>("QNetworkConfiguration");
qRegisterMetaType<QNetworkConfigurationPrivatePointer>("QNetworkConfigurationPrivatePointer");
@@ -93,13 +93,12 @@ void QNetworkConfigurationManagerPrivate::cleanup()
delete thread;
}
-QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration()
+QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const
{
QMutexLocker locker(&mutex);
foreach (QBearerEngine *engine, sessionEngines) {
QNetworkConfigurationPrivatePointer ptr = engine->defaultConfiguration();
-
if (ptr) {
QNetworkConfiguration config;
config.d = ptr;
@@ -118,8 +117,8 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration(
QMutexLocker locker(&engine->mutex);
- for (it = engine->snapConfigurations.begin(), end = engine->snapConfigurations.end();
- it != end; ++it) {
+ for (it = engine->snapConfigurations.begin(),
+ end = engine->snapConfigurations.end(); it != end; ++it) {
QNetworkConfigurationPrivatePointer ptr = it.value();
QMutexLocker configLocker(&ptr->mutex);
@@ -129,10 +128,8 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration(
config.d = ptr;
return config;
} else if (!defaultConfiguration) {
- if ((ptr->state & QNetworkConfiguration::Discovered) ==
- QNetworkConfiguration::Discovered) {
+ if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered)
defaultConfiguration = ptr;
- }
}
}
}
@@ -156,8 +153,6 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration(
6. Discovered Other
*/
- defaultConfiguration.reset();
-
foreach (QBearerEngine *engine, sessionEngines) {
QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it;
QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end;
@@ -171,8 +166,7 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration(
QMutexLocker configLocker(&ptr->mutex);
QNetworkConfiguration::BearerType bearerType = ptr->bearerType;
- if ((ptr->state & QNetworkConfiguration::Discovered) ==
- QNetworkConfiguration::Discovered) {
+ if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) {
if (!defaultConfiguration) {
defaultConfiguration = ptr;
} else {
@@ -216,7 +210,7 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration(
return QNetworkConfiguration();
}
-QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter)
+QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) const
{
QList<QNetworkConfiguration> result;
@@ -260,7 +254,7 @@ QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurati
return result;
}
-QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier)
+QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) const
{
QNetworkConfiguration item;
@@ -270,11 +264,11 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIden
QMutexLocker locker(&engine->mutex);
if (engine->accessPointConfigurations.contains(identifier))
- item.d = engine->accessPointConfigurations.value(identifier);
+ item.d = engine->accessPointConfigurations[identifier];
else if (engine->snapConfigurations.contains(identifier))
- item.d = engine->snapConfigurations.value(identifier);
+ item.d = engine->snapConfigurations[identifier];
else if (engine->userChoiceConfigurations.contains(identifier))
- item.d = engine->userChoiceConfigurations.value(identifier);
+ item.d = engine->userChoiceConfigurations[identifier];
else
continue;
@@ -284,14 +278,14 @@ QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIden
return item;
}
-bool QNetworkConfigurationManagerPrivate::isOnline()
+bool QNetworkConfigurationManagerPrivate::isOnline() const
{
QMutexLocker locker(&mutex);
return !onlineConfigurations.isEmpty();
}
-QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities()
+QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() const
{
QMutexLocker locker(&mutex);
@@ -373,16 +367,15 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations()
QMutexLocker locker(&mutex);
if (firstUpdate) {
- if (sender())
+ if (qobject_cast<QBearerEngine *>(sender()))
return;
updating = false;
#ifndef QT_NO_LIBRARY
- QFactoryLoader *l = loader();
-
QBearerEngine *generic = 0;
+ QFactoryLoader *l = loader();
foreach (const QString &key, l->keys()) {
QBearerEnginePlugin *plugin = qobject_cast<QBearerEnginePlugin *>(l->instance(key));
if (plugin) {
@@ -414,11 +407,8 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations()
}
QBearerEngine *engine = qobject_cast<QBearerEngine *>(sender());
- if (!updatingEngines.isEmpty() && engine) {
- int index = sessionEngines.indexOf(engine);
- if (index >= 0)
- updatingEngines.remove(index);
- }
+ if (engine && !updatingEngines.isEmpty())
+ updatingEngines.remove(engine);
if (updating && updatingEngines.isEmpty()) {
updating = false;
@@ -426,10 +416,7 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations()
}
if (engine && !pollingEngines.isEmpty()) {
- int index = sessionEngines.indexOf(engine);
- if (index >= 0)
- pollingEngines.remove(index);
-
+ pollingEngines.remove(engine);
if (pollingEngines.isEmpty())
startPolling();
}
@@ -455,13 +442,13 @@ void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate()
updating = true;
- for (int i = 0; i < sessionEngines.count(); ++i) {
- updatingEngines.insert(i);
- QMetaObject::invokeMethod(sessionEngines.at(i), "requestUpdate");
+ foreach (QBearerEngine *engine, sessionEngines) {
+ updatingEngines.insert(engine);
+ QMetaObject::invokeMethod(engine, "requestUpdate");
}
}
-QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines()
+QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const
{
QMutexLocker locker(&mutex);
@@ -472,35 +459,21 @@ void QNetworkConfigurationManagerPrivate::startPolling()
{
QMutexLocker locker(&mutex);
- bool pollingRequired = false;
-
- if (forcedPolling > 0) {
- foreach (QBearerEngine *engine, sessionEngines) {
- if (engine->requiresPolling()) {
- pollingRequired = true;
- break;
- }
- }
+ if(!pollTimer) {
+ pollTimer = new QTimer(this);
+ pollTimer->setInterval(10000);
+ pollTimer->setSingleShot(true);
+ connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollEngines()));
}
- if (!pollingRequired) {
- foreach (QBearerEngine *engine, sessionEngines) {
- if (engine->configurationsInUse()) {
- pollingRequired = true;
- break;
- }
- }
- }
+ if(pollTimer->isActive())
+ return;
- if (pollingRequired) {
- if (!pollTimer) {
- pollTimer = new QTimer(this);
- pollTimer->setInterval(10000);
- pollTimer->setSingleShot(true);
- connect(pollTimer, SIGNAL(timeout()), this, SLOT(pollEngines()));
+ foreach (QBearerEngine *engine, sessionEngines) {
+ if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
+ pollTimer->start();
+ break;
}
-
- pollTimer->start();
}
}
@@ -508,13 +481,10 @@ void QNetworkConfigurationManagerPrivate::pollEngines()
{
QMutexLocker locker(&mutex);
- for (int i = 0; i < sessionEngines.count(); ++i) {
- if (!sessionEngines.at(i)->requiresPolling())
- continue;
-
- if (forcedPolling || sessionEngines.at(i)->configurationsInUse()) {
- pollingEngines.insert(i);
- QMetaObject::invokeMethod(sessionEngines.at(i), "requestUpdate");
+ foreach (QBearerEngine *engine, sessionEngines) {
+ if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
+ pollingEngines.insert(engine);
+ QMetaObject::invokeMethod(engine, "requestUpdate");
}
}
}
diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h
index b3c83bc..56f66b4 100644
--- a/src/network/bearer/qnetworkconfigmanager_p.h
+++ b/src/network/bearer/qnetworkconfigmanager_p.h
@@ -74,60 +74,58 @@ public:
QNetworkConfigurationManagerPrivate();
virtual ~QNetworkConfigurationManagerPrivate();
- QNetworkConfiguration defaultConfiguration();
- QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter);
- QNetworkConfiguration configurationFromIdentifier(const QString &identifier);
+ QNetworkConfiguration defaultConfiguration() const;
+ QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter) const;
+ QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const;
- bool isOnline();
+ bool isOnline() const;
- QNetworkConfigurationManager::Capabilities capabilities();
+ QNetworkConfigurationManager::Capabilities capabilities() const;
void performAsyncConfigurationUpdate();
- QList<QBearerEngine *> engines();
-
- Q_INVOKABLE void startPolling();
+ QList<QBearerEngine *> engines() const;
void enablePolling();
void disablePolling();
void initialize();
void cleanup();
-public slots:
+public Q_SLOTS:
void updateConfigurations();
Q_SIGNALS:
- void configurationAdded(const QNetworkConfiguration& config);
- void configurationRemoved(const QNetworkConfiguration& config);
+ void configurationAdded(const QNetworkConfiguration &config);
+ void configurationRemoved(const QNetworkConfiguration &config);
+ void configurationChanged(const QNetworkConfiguration &config);
void configurationUpdateComplete();
- void configurationChanged(const QNetworkConfiguration& config);
void onlineStateChanged(bool isOnline);
- void abort();
+private Q_SLOTS:
+ void configurationAdded(QNetworkConfigurationPrivatePointer ptr);
+ void configurationRemoved(QNetworkConfigurationPrivatePointer ptr);
+ void configurationChanged(QNetworkConfigurationPrivatePointer ptr);
+
+ void pollEngines();
private:
+ Q_INVOKABLE void startPolling();
QTimer *pollTimer;
QThread *bearerThread;
- QMutex mutex;
+private:
+ mutable QMutex mutex;
QList<QBearerEngine *> sessionEngines;
QSet<QString> onlineConfigurations;
- QSet<int> pollingEngines;
- QSet<int> updatingEngines;
+ QSet<QBearerEngine *> pollingEngines;
+ QSet<QBearerEngine *> updatingEngines;
int forcedPolling;
bool updating;
bool firstUpdate;
-
-private Q_SLOTS:
- void configurationAdded(QNetworkConfigurationPrivatePointer ptr);
- void configurationRemoved(QNetworkConfigurationPrivatePointer ptr);
- void configurationChanged(QNetworkConfigurationPrivatePointer ptr);
-
- void pollEngines();
};
Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate();
@@ -136,4 +134,4 @@ QT_END_NAMESPACE
#endif // QT_NO_BEARERMANAGEMENT
-#endif //QNETWORKCONFIGURATIONMANAGERPRIVATE_H
+#endif // QNETWORKCONFIGURATIONMANAGERPRIVATE_H
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
index 23a212e..e207feb 100644
--- a/src/network/bearer/qnetworkconfiguration.cpp
+++ b/src/network/bearer/qnetworkconfiguration.cpp
@@ -212,44 +212,38 @@ QNetworkConfiguration::QNetworkConfiguration()
/*!
Creates a copy of the QNetworkConfiguration object contained in \a other.
*/
-QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration& other)
+QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration &other)
: d(other.d)
{
}
/*!
- Copies the content of the QNetworkConfiguration object contained in \a other into this one.
+ Frees the resources associated with the QNetworkConfiguration object.
*/
-QNetworkConfiguration& QNetworkConfiguration::operator=(const QNetworkConfiguration& other)
+QNetworkConfiguration::~QNetworkConfiguration()
{
- d = other.d;
- return *this;
}
/*!
- Frees the resources associated with the QNetworkConfiguration object.
+ Copies the content of the QNetworkConfiguration object contained in \a other into this one.
*/
-QNetworkConfiguration::~QNetworkConfiguration()
+QNetworkConfiguration &QNetworkConfiguration::operator=(const QNetworkConfiguration &other)
{
+ d = other.d;
+ return *this;
}
/*!
Returns true, if this configuration is the same as the \a other
configuration given; otherwise returns false.
*/
-bool QNetworkConfiguration::operator==(const QNetworkConfiguration& other) const
+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
+ \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.
@@ -370,11 +364,14 @@ QList<QNetworkConfiguration> QNetworkConfiguration::children() const
{
QList<QNetworkConfiguration> results;
- if (type() != QNetworkConfiguration::ServiceNetwork || !isValid())
+ if (!d)
return results;
QMutexLocker locker(&d->mutex);
+ if (d->type != QNetworkConfiguration::ServiceNetwork || !d->isValid)
+ return results;
+
QMutableMapIterator<unsigned int, QNetworkConfigurationPrivatePointer> i(d->serviceNetworkMembers);
while (i.hasNext()) {
i.next();
@@ -510,4 +507,3 @@ QString QNetworkConfiguration::bearerTypeName() const
}
QT_END_NAMESPACE
-
diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h
index 9942988..86de72b 100644
--- a/src/network/bearer/qnetworkconfiguration.h
+++ b/src/network/bearer/qnetworkconfiguration.h
@@ -73,12 +73,12 @@ class QNetworkConfigurationExport QNetworkConfiguration
public:
QNetworkConfiguration();
QNetworkConfiguration(const QNetworkConfiguration& other);
- QNetworkConfiguration &operator=(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); }
+ bool operator==(const QNetworkConfiguration &other) const;
+ inline bool operator!=(const QNetworkConfiguration &other) const
+ { return !operator==(other); }
enum Type {
InternetAccessPoint = 0,
@@ -100,7 +100,6 @@ public:
Discovered = 0x0000006,
Active = 0x000000e
};
-
Q_DECLARE_FLAGS(StateFlags, StateFlag)
#ifndef QT_MOBILITY_BEARER
@@ -155,4 +154,4 @@ QTM_END_NAMESPACE
QT_END_HEADER
-#endif //QNETWORKCONFIGURATION_H
+#endif // QNETWORKCONFIGURATION_H
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
index 4a78e15..edb7105 100644
--- a/src/network/bearer/qnetworkconfiguration_p.h
+++ b/src/network/bearer/qnetworkconfiguration_p.h
@@ -65,18 +65,17 @@ typedef QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfi
class QNetworkConfigurationPrivate : public QSharedData
{
public:
- QNetworkConfigurationPrivate ()
- : mutex(QMutex::Recursive), type(QNetworkConfiguration::Invalid),
+ QNetworkConfigurationPrivate() :
+ mutex(QMutex::Recursive),
+ type(QNetworkConfiguration::Invalid),
purpose(QNetworkConfiguration::UnknownPurpose),
bearerType(QNetworkConfiguration::BearerUnknown),
isValid(false), roamingSupported(false)
- {
- }
-
+ {}
virtual ~QNetworkConfigurationPrivate()
{
//release pointers to member configurations
- serviceNetworkMembers.clear();
+ serviceNetworkMembers.clear();
}
virtual QString bearerTypeName() const
@@ -100,11 +99,9 @@ public:
bool roamingSupported;
private:
- // disallow detaching
- QNetworkConfigurationPrivate &operator=(const QNetworkConfigurationPrivate &other);
- QNetworkConfigurationPrivate(const QNetworkConfigurationPrivate &other);
+ Q_DISABLE_COPY(QNetworkConfigurationPrivate)
};
QT_END_NAMESPACE
-#endif //QNETWORKCONFIGURATIONPRIVATE_H
+#endif // QNETWORKCONFIGURATIONPRIVATE_H
diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp
index 5db2494..5d2dd36 100644
--- a/src/network/bearer/qnetworksession.cpp
+++ b/src/network/bearer/qnetworksession.cpp
@@ -39,14 +39,21 @@
**
****************************************************************************/
+#include "qnetworksession.h"
+#include "qbearerengine_p.h"
+
#include <QEventLoop>
#include <QTimer>
+#include <QThread>
-#include "qnetworksession.h"
-#include "qbearerengine_p.h"
#include "qnetworkconfigmanager_p.h"
#include "qnetworksession_p.h"
+#ifdef Q_OS_SYMBIAN
+#include <es_sock.h>
+#include <private/qcore_symbian_p.h>
+#endif
+
#ifndef QT_NO_BEARERMANAGEMENT
QT_BEGIN_NAMESPACE
@@ -165,7 +172,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QNetworkSession::preferredConfigurationChanged(const QNetworkConfiguration& config, bool isSeamless)
+ \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
@@ -224,30 +231,29 @@ QT_BEGIN_NAMESPACE
\sa QNetworkConfiguration
*/
-QNetworkSession::QNetworkSession(const QNetworkConfiguration& connectionConfig, QObject* parent)
-: QObject(parent), d(0)
+QNetworkSession::QNetworkSession(const QNetworkConfiguration &connectionConfig, QObject *parent)
+ : QObject(parent), d(0)
{
// invalid configuration
- if (connectionConfig.identifier().isNull())
- return;
-
- 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;
+ if (!connectionConfig.identifier().isEmpty()) {
+ 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;
+ }
}
}
@@ -317,19 +323,16 @@ bool QNetworkSession::waitForOpened(int msecs)
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()));
+ QEventLoop loop;
+ 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()));
+ if (msecs >= 0)
+ QTimer::singleShot(msecs, &loop, SLOT(quit()));
- loop->exec();
- loop->disconnect();
- loop->deleteLater();
+ // enter the event loop and wait for opened/error/timeout
+ loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
return d->isOpen;
}
@@ -475,7 +478,7 @@ QString QNetworkSession::errorString() const
\code
QNetworkConfigurationManager mgr;
QNetworkConfiguration ap = mgr.defaultConfiguration();
- QNetworkSession* session = new QNetworkSession(ap);
+ QNetworkSession *session = new QNetworkSession(ap);
... //code activates session
QString ident = session->sessionProperty("ActiveConfiguration").toString();
@@ -520,20 +523,13 @@ QString QNetworkSession::errorString() const
has no effect for sessions that do not require polling.
\endtable
*/
-QVariant QNetworkSession::sessionProperty(const QString& key) const
+QVariant QNetworkSession::sessionProperty(const QString &key) const
{
- if (!d)
+ if (!d || !d->publicConfig.isValid())
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("ActiveConfiguration"))
+ return d->isOpen ? d->activeConfig.identifier() : QString();
if (key == QLatin1String("UserChoiceConfiguration")) {
if (!d->isOpen || d->publicConfig.type() != QNetworkConfiguration::UserChoice)
@@ -556,7 +552,7 @@ QVariant QNetworkSession::sessionProperty(const QString& key) const
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)
+void QNetworkSession::setSessionProperty(const QString &key, const QVariant &value)
{
if (!d)
return;
@@ -590,7 +586,7 @@ void QNetworkSession::migrate()
*/
void QNetworkSession::ignore()
{
- // Needed on at least Symbian platform: the roaming must be explicitly
+ // Needed on at least Symbian platform: the roaming must be explicitly
// ignore()'d or migrate()'d
if (d)
d->ignore();
@@ -684,11 +680,12 @@ quint64 QNetworkSession::activeTime() const
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;
+ //check for preferredConfigurationChanged() signal connect notification
+ //This is not required on all platforms
if (qstrcmp(signal, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) == 0)
d->setALREnabled(true);
}
@@ -696,23 +693,58 @@ void QNetworkSession::connectNotify(const char *signal)
/*!
\internal
- This function is called when the client disconnects from the preferredConfigurationChanged()
- signal.
+ 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;
+ //check for preferredConfigurationChanged() signal disconnect notification
+ //This is not required on all platforms
if (qstrcmp(signal, SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool))) == 0)
d->setALREnabled(false);
}
+#ifdef Q_OS_SYMBIAN
+RConnection* QNetworkSessionPrivate::nativeSession(QNetworkSession &s)
+{
+ if (!s.d)
+ return 0;
+ if (s.thread() != QThread::currentThread())
+ qWarning("QNetworkSessionPrivate::nativeSession called in wrong thread");
+ return s.d->nativeSession();
+}
+
+TInt QNetworkSessionPrivate::nativeOpenSocket(QNetworkSession& s, RSocket& sock, TUint family, TUint type, TUint protocol)
+{
+ if (!s.d)
+ return 0;
+ QMutexLocker lock(&(s.d->mutex));
+ RConnection *con = s.d->nativeSession();
+ if (!con || !con->SubSessionHandle())
+ return KErrNotReady;
+ return sock.Open(qt_symbianGetSocketServer(), family, type, protocol, *con);
+}
+
+TInt QNetworkSessionPrivate::nativeOpenHostResolver(QNetworkSession& s, RHostResolver& resolver, TUint family, TUint protocol)
+{
+ if (!s.d)
+ return 0;
+ QMutexLocker lock(&(s.d->mutex));
+ RConnection *con = s.d->nativeSession();
+ if (!con || !con->SubSessionHandle())
+ return KErrNotReady;
+ return resolver.Open(qt_symbianGetSocketServer(), family, protocol, *con);
+}
+
+#endif
+
#include "moc_qnetworksession.cpp"
QT_END_NAMESPACE
diff --git a/src/network/bearer/qnetworksession.h b/src/network/bearer/qnetworksession.h
index d16b1bc..4bc06ee 100644
--- a/src/network/bearer/qnetworksession.h
+++ b/src/network/bearer/qnetworksession.h
@@ -71,6 +71,7 @@ class QNetworkSessionPrivate;
class QNetworkSessionExport QNetworkSession : public QObject
{
Q_OBJECT
+
public:
enum State {
Invalid = 0,
@@ -89,7 +90,8 @@ public:
OperationNotSupportedError,
InvalidConfigurationError
};
- explicit QNetworkSession(const QNetworkConfiguration& connConfig, QObject* parent =0);
+
+ explicit QNetworkSession(const QNetworkConfiguration &connConfig, QObject *parent = 0);
virtual ~QNetworkSession();
bool isOpen() const;
@@ -101,8 +103,8 @@ public:
State state() const;
SessionError error() const;
QString errorString() const;
- QVariant sessionProperty(const QString& key) const;
- void setSessionProperty(const QString& key, const QVariant& value);
+ QVariant sessionProperty(const QString &key) const;
+ void setSessionProperty(const QString &key, const QVariant &value);
quint64 bytesWritten() const;
quint64 bytesReceived() const;
@@ -121,13 +123,12 @@ public Q_SLOTS:
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 preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless);
void newConfigurationActivated();
protected:
@@ -135,9 +136,9 @@ protected:
virtual void disconnectNotify(const char *signal);
private:
- QNetworkSessionPrivate* d;
friend class QNetworkSessionPrivate;
- };
+ QNetworkSessionPrivate *d;
+};
#ifndef QT_MOBILITY_BEARER
QT_END_NAMESPACE
@@ -151,4 +152,4 @@ QT_END_HEADER
#endif // QT_NO_BEARERMANAGEMENT
-#endif //QNETWORKSESSION_H
+#endif // QNETWORKSESSION_H
diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h
index 733756a..cfd7444 100644
--- a/src/network/bearer/qnetworksession_p.h
+++ b/src/network/bearer/qnetworksession_p.h
@@ -55,9 +55,16 @@
#include "qnetworksession.h"
#include "qnetworkconfiguration_p.h"
+#include "QtCore/qsharedpointer.h"
#ifndef QT_NO_BEARERMANAGEMENT
+#ifdef Q_OS_SYMBIAN
+class RConnection;
+class RSocket;
+class RHostResolver;
+#endif
+
QT_BEGIN_NAMESPACE
class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject
@@ -67,14 +74,11 @@ class Q_NETWORK_EXPORT QNetworkSessionPrivate : public QObject
friend class QNetworkSession;
public:
- QNetworkSessionPrivate()
- : state(QNetworkSession::Invalid), isOpen(false)
- {
- }
-
+ QNetworkSessionPrivate() : QObject(),
+ state(QNetworkSession::Invalid), isOpen(false), mutex(QMutex::Recursive)
+ {}
virtual ~QNetworkSessionPrivate()
- {
- }
+ {}
//called by QNetworkSession constructor and ensures
//that the state is immediately updated (w/o actually opening
@@ -85,14 +89,14 @@ public:
#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 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 setALREnabled(bool /*enabled*/) {}
virtual void migrate() = 0;
virtual void accept() = 0;
virtual void ignore() = 0;
@@ -105,6 +109,15 @@ public:
virtual quint64 bytesReceived() const = 0;
virtual quint64 activeTime() const = 0;
+#ifdef Q_OS_SYMBIAN
+ // get internal RConnection (not thread safe, call only from thread that owns the QNetworkSession)
+ static RConnection* nativeSession(QNetworkSession&);
+ virtual RConnection* nativeSession() = 0;
+ // open socket using the internal RConnection (thread safe)
+ static TInt nativeOpenSocket(QNetworkSession& session, RSocket& socket, TUint family, TUint type, TUint protocol);
+ // open host resolver using the internal RConnection (thread safe)
+ static TInt nativeOpenHostResolver(QNetworkSession& session, RHostResolver& resolver, TUint family, TUint protocol);
+#endif
protected:
inline QNetworkConfigurationPrivatePointer privateConfiguration(const QNetworkConfiguration &config) const
{
@@ -144,11 +157,14 @@ protected:
QNetworkSession::State state;
bool isOpen;
+
+ QMutex mutex;
};
QT_END_NAMESPACE
-#endif // QT_NO_BEARERMANAGEMENT
+Q_DECLARE_METATYPE(QSharedPointer<QNetworkSession>)
-#endif //QNETWORKSESSIONPRIVATE_H
+#endif // QT_NO_BEARERMANAGEMENT
+#endif // QNETWORKSESSIONPRIVATE_H
diff --git a/src/network/bearer/qsharednetworksession_p.h b/src/network/bearer/qsharednetworksession_p.h
index 2a344d6..31b61b5 100644
--- a/src/network/bearer/qsharednetworksession_p.h
+++ b/src/network/bearer/qsharednetworksession_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
@@ -64,6 +64,8 @@
QT_BEGIN_NAMESPACE
+uint qHash(const QNetworkConfiguration& config);
+
class QSharedNetworkSessionManager
{
public:
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 66e87c9..e5d33bb 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -20,12 +20,13 @@ SOURCES += kernel/qauthenticator.cpp \
kernel/qnetworkproxy.cpp \
kernel/qnetworkinterface.cpp
-symbian: SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_symbian.cpp
+symbian: SOURCES += kernel/qhostinfo_symbian.cpp kernel/qnetworkinterface_symbian.cpp
unix:!symbian:SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
win32:SOURCES += kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp
+integrity:SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp
mac:LIBS_PRIVATE += -framework SystemConfiguration -framework CoreFoundation
-mac:SOURCES += kernel/qnetworkproxy_mac.cpp
+mac:contains(QT_CONFIG, coreservices) SOURCES += kernel/qnetworkproxy_mac.cpp
else:win32:SOURCES += kernel/qnetworkproxy_win.cpp
else:symbian:SOURCES += kernel/qnetworkproxy_symbian.cpp
else:SOURCES += kernel/qnetworkproxy_generic.cpp
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 818aab7..0423e22 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -223,7 +223,7 @@ void QAuthenticator::setUser(const QString &user)
} else if((separatorPosn = user.indexOf(QLatin1String("@"))) != -1) {
//domain name is present
d->realm.clear();
- d->userDomain = user.left(separatorPosn);
+ d->userDomain = user.mid(separatorPosn + 1);
d->extractedUser = user.left(separatorPosn);
d->user = user;
} else {
@@ -395,7 +395,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
case Basic:
if(realm.isEmpty())
this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
- if (user.isEmpty())
+ if (user.isEmpty() && password.isEmpty())
phase = Done;
break;
case Ntlm:
@@ -406,7 +406,7 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
if (options.value("stale").toLower() == "true")
phase = Start;
- if (user.isEmpty())
+ if (user.isEmpty() && password.isEmpty())
phase = Done;
break;
}
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index f55f383..7eeb4e5 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -720,7 +720,8 @@ Q_IPV6ADDR QHostAddress::toIPv6Address() const
Returns the address as a string.
For example, if the address is the IPv4 address 127.0.0.1, the
- returned string is "127.0.0.1".
+ returned string is "127.0.0.1". For IPv6 the string format will
+ follow the RFC5952 recommendation.
\sa toIPv4Address()
*/
@@ -741,8 +742,32 @@ QString QHostAddress::toString() const
ugle[i] = (quint16(d->a6[2*i]) << 8) | quint16(d->a6[2*i+1]);
}
QString s;
- s.sprintf("%X:%X:%X:%X:%X:%X:%X:%X",
- ugle[0], ugle[1], ugle[2], ugle[3], ugle[4], ugle[5], ugle[6], ugle[7]);
+ QString temp;
+ bool zeroDetected = false;
+ bool zeroShortened = false;
+ for (int i = 0; i < 8; i++) {
+ if ((ugle[i] != 0) || zeroShortened) {
+ temp.sprintf("%X", ugle[i]);
+ s.append(temp);
+ if (zeroDetected)
+ zeroShortened = true;
+ } else {
+ if (!zeroDetected) {
+ if (i<7 && (ugle[i+1] == 0)) {
+ s.append(QLatin1Char(':'));
+ zeroDetected = true;
+ } else {
+ temp.sprintf("%X", ugle[i]);
+ s.append(temp);
+ if (i<7)
+ s.append(QLatin1Char(':'));
+ }
+ }
+ }
+ if (i<7 && ((ugle[i] != 0) || zeroShortened || (i==0 && zeroDetected)))
+ s.append(QLatin1Char(':'));
+ }
+
if (!d->scopeId.isEmpty())
s.append(QLatin1Char('%') + d->scopeId);
return s;
diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h
index 255d706..0349ff3 100644
--- a/src/network/kernel/qhostaddress_p.h
+++ b/src/network/kernel/qhostaddress_p.h
@@ -71,6 +71,11 @@ public:
void setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int len);
};
+#ifdef Q_OS_SYMBIAN
+class TInetAddr;
+QHostAddress qt_QHostAddressFromTInetAddr(const TInetAddr& addr);
+#endif
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 94fd692..d42c259 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -49,6 +49,7 @@
#include <qstringlist.h>
#include <qthread.h>
#include <qurl.h>
+#include <private/qnetworksession_p.h>
#ifdef Q_OS_UNIX
# include <unistd.h>
@@ -56,12 +57,14 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_THREAD
+//#define QHOSTINFO_DEBUG
+
+#ifndef Q_OS_SYMBIAN
Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
+#else
+Q_GLOBAL_STATIC(QSymbianHostInfoLookupManager, theHostInfoLookupManager)
#endif
-//#define QHOSTINFO_DEBUG
-
/*!
\class QHostInfo
\brief The QHostInfo class provides static functions for host name lookups.
@@ -87,10 +90,8 @@ Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
\snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0
- The slot is invoked when the results are ready. (If you use
- Qt for Embedded Linux and disabled multithreading support by defining
- \c QT_NO_THREAD, lookupHost() will block until the lookup has
- finished.) The results are stored in a QHostInfo object. Call
+ The slot is invoked when the results are ready. The results are
+ stored in a QHostInfo object. Call
addresses() to get the list of IP addresses for the host, and
hostName() to get the host name that was looked up.
@@ -156,6 +157,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
qDebug("QHostInfo::lookupHost(\"%s\", %p, %s)",
name.toLatin1().constData(), receiver, member ? member + 1 : 0);
#endif
+
if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
qWarning("QHostInfo::lookupHost() called with no event dispatcher");
return -1;
@@ -176,15 +178,9 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
return id;
}
-#ifdef QT_NO_THREAD
- QHostInfo hostInfo = QHostInfoAgent::fromName(name);
- hostInfo.setLookupId(id);
- QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
- QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
- receiver, member, Qt::QueuedConnection);
- result.data()->emitResultsReady(hostInfo);
-#else
+#ifndef Q_OS_SYMBIAN
QHostInfoLookupManager *manager = theHostInfoLookupManager();
+
if (manager) {
// the application is still alive
if (manager->cache.isEnabled()) {
@@ -199,11 +195,43 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
return id;
}
}
+
// cache is not enabled or it was not in the cache, do normal lookup
QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
manager->scheduleLookup(runnable);
}
+#else
+ QSymbianHostInfoLookupManager *manager = theHostInfoLookupManager();
+
+ if (manager) {
+ // the application is still alive
+ if (manager->cache.isEnabled()) {
+ // check cache first
+ bool valid = false;
+ QHostInfo info = manager->cache.get(name, &valid);
+ if (valid) {
+ info.setLookupId(id);
+ QHostInfoResult result;
+ QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
+ result.emitResultsReady(info);
+ return id;
+ }
+ }
+
+ // cache is not enabled or it was not in the cache, do normal lookup
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+ QVariant v(receiver->property("_q_networksession"));
+ if (v.isValid())
+ networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v);
+#endif
+
+ QSymbianHostResolver *symbianResolver = 0;
+ QT_TRAP_THROWING(symbianResolver = new QSymbianHostResolver(name, id, networkSession));
+ QObject::connect(&symbianResolver->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
+ manager->scheduleLookup(symbianResolver);
+ }
#endif
return id;
@@ -216,12 +244,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
*/
void QHostInfo::abortHostLookup(int id)
{
-#ifndef QT_NO_THREAD
theHostInfoLookupManager()->abortLookup(id);
-#else
- // we cannot abort if it was non threaded.. the result signal has already been posted
- Q_UNUSED(id);
-#endif
}
/*!
@@ -244,10 +267,33 @@ QHostInfo QHostInfo::fromName(const QString &name)
#endif
QHostInfo hostInfo = QHostInfoAgent::fromName(name);
- QHostInfoLookupManager *manager = theHostInfoLookupManager();
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ manager->cache.put(name, hostInfo);
+ return hostInfo;
+}
+
+#ifndef QT_NO_BEARERMANAGEMENT
+QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetworkSession> session)
+{
+#if defined QHOSTINFO_DEBUG
+ qDebug("QHostInfoPrivate::fromName(\"%s\") with session %p",name.toLatin1().constData(), session.data());
+#endif
+
+ QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
manager->cache.put(name, hostInfo);
return hostInfo;
}
+#endif
+
+#ifndef Q_OS_SYMBIAN
+// This function has a special implementation for symbian right now in qhostinfo_symbian.cpp but not on other OS.
+QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession>)
+{
+ return QHostInfoAgent::fromName(hostName);
+}
+#endif
+
/*!
\enum QHostInfo::HostInfoError
@@ -425,7 +471,7 @@ void QHostInfo::setErrorString(const QString &str)
\sa hostName()
*/
-#ifndef QT_NO_THREAD
+#ifndef Q_OS_SYMBIAN
QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
{
setAutoDelete(true);
@@ -653,6 +699,7 @@ void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
finishedLookups.append(r);
work();
}
+#endif
// This function returns immediately when we had a result in the cache, else it will later emit a signal
QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id)
@@ -661,7 +708,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char
*id = -1;
// check cache
- QHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
if (manager && manager->cache.isEnabled()) {
QHostInfo info = manager->cache.get(name, valid);
if (*valid) {
@@ -678,7 +725,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char
void qt_qhostinfo_clear_cache()
{
- QHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
if (manager) {
manager->clear();
}
@@ -686,7 +733,7 @@ void qt_qhostinfo_clear_cache()
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
{
- QHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
if (manager) {
manager->cache.setEnabled(e);
}
@@ -754,6 +801,9 @@ void QHostInfoCache::clear()
cache.clear();
}
-#endif // QT_NO_THREAD
+QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
+{
+ return theHostInfoLookupManager();
+}
QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index e1abc9d..035aa01 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -60,8 +60,6 @@
#include "QtCore/qwaitcondition.h"
#include "QtCore/qobject.h"
#include "QtCore/qpointer.h"
-
-#ifndef QT_NO_THREAD
#include "QtCore/qthread.h"
#include "QtCore/qthreadpool.h"
#include "QtCore/qmutex.h"
@@ -70,10 +68,20 @@
#include "QtCore/qqueue.h"
#include <QElapsedTimer>
#include <QCache>
+
+#include <QNetworkSession>
+#include <QSharedPointer>
+
+#ifdef Q_OS_SYMBIAN
+// Symbian Headers
+#include <es_sock.h>
+#include <in_sock.h>
#endif
+
QT_BEGIN_NAMESPACE
+
class QHostInfoResult : public QObject
{
Q_OBJECT
@@ -93,6 +101,12 @@ class QHostInfoAgent : public QObject
Q_OBJECT
public:
static QHostInfo fromName(const QString &hostName);
+ static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession);
+
+#ifdef Q_OS_SYMBIAN
+ static int lookupHost(const QString &name, QObject *receiver, const char *member);
+ static void abortHostLookup(int lookupId);
+#endif
};
class QHostInfoPrivate
@@ -104,6 +118,10 @@ public:
lookupId(0)
{
}
+#ifndef QT_NO_BEARERMANAGEMENT
+ //not a public API yet
+ static QHostInfo fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession);
+#endif
QHostInfo::HostInfoError err;
QString errorStr;
@@ -112,7 +130,6 @@ public:
int lookupId;
};
-#ifndef QT_NO_THREAD
// These functions are outside of the QHostInfo class and strictly internal.
// Do NOT use them outside of QAbstractSocket.
QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id);
@@ -154,7 +171,25 @@ public:
QHostInfoResult resultEmitter;
};
-class QHostInfoLookupManager : public QObject
+
+class QAbstractHostInfoLookupManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ ~QAbstractHostInfoLookupManager() {}
+ virtual void clear() = 0;
+
+ QHostInfoCache cache;
+
+protected:
+ QAbstractHostInfoLookupManager() {}
+ static QAbstractHostInfoLookupManager* globalInstance();
+
+};
+
+#ifndef Q_OS_SYMBIAN
+class QHostInfoLookupManager : public QAbstractHostInfoLookupManager
{
Q_OBJECT
public:
@@ -172,8 +207,6 @@ public:
void lookupFinished(QHostInfoRunnable *r);
bool wasAborted(int id);
- QHostInfoCache cache;
-
friend class QHostInfoRunnable;
protected:
QList<QHostInfoRunnable*> currentLookups; // in progress
@@ -192,8 +225,95 @@ private slots:
void waitForThreadPoolDone() { threadPool.waitForDone(); }
};
+#else
+
+class QSymbianHostResolver : public CActive
+{
+public:
+ QSymbianHostResolver(const QString &hostName, int id, QSharedPointer<QNetworkSession> networkSession);
+ ~QSymbianHostResolver();
+
+ void requestHostLookup();
+ void abortHostLookup();
+ int id();
+
+ void returnResults();
+
+ QHostInfoResult resultEmitter;
+
+private:
+ void DoCancel();
+ void RunL();
+ void run();
+ TInt RunError(TInt aError);
+
+ void processNameResult();
+ void nextNameResult();
+ void processAddressResult();
+
+private:
+ int iId;
+
+ const QString iHostName;
+ QString iEncodedHostName;
+ TPtrC iHostNamePtr;
+
+ RSocketServ& iSocketServ;
+ RHostResolver iHostResolver;
+ QSharedPointer<QNetworkSession> iNetworkSession;
+
+ TNameEntry iNameResult;
+ TInetAddr IpAdd;
+
+ QHostAddress iAddress;
+
+ QHostInfo iResults;
+
+ QList<QHostAddress> iHostAddresses;
+
+ enum {
+ EIdle,
+ EGetByName,
+ EGetByAddress,
+ ECompleteFromCache,
+ EError
+ } iState;
+};
+
+class QSymbianHostInfoLookupManager : public QAbstractHostInfoLookupManager
+{
+ Q_OBJECT
+public:
+ QSymbianHostInfoLookupManager();
+ ~QSymbianHostInfoLookupManager();
+
+ static QSymbianHostInfoLookupManager* globalInstance();
+
+ int id();
+ void clear();
+
+ // called from QHostInfo
+ void scheduleLookup(QSymbianHostResolver *r);
+ void abortLookup(int id);
+
+ // called from QSymbianHostResolver
+ void lookupFinished(QSymbianHostResolver *r);
+
+private:
+ void runNextLookup();
+
+ // this is true for single threaded use, with multiple threads the max is ((number of threads) + KMaxConcurrentLookups - 1)
+ static const int KMaxConcurrentLookups = 5;
+
+ QList<QSymbianHostResolver*> iCurrentLookups;
+ QList<QSymbianHostResolver*> iScheduledLookups;
+
+ QMutex mutex;
+};
#endif
+
+
QT_END_NAMESPACE
#endif // QHOSTINFO_P_H
diff --git a/src/network/kernel/qhostinfo_symbian.cpp b/src/network/kernel/qhostinfo_symbian.cpp
new file mode 100644
index 0000000..86c157c
--- /dev/null
+++ b/src/network/kernel/qhostinfo_symbian.cpp
@@ -0,0 +1,591 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHOSTINFO_DEBUG
+
+// Qt Headers
+#include <QByteArray>
+#include <QUrl>
+#include <QList>
+
+#include "qplatformdefs.h"
+
+#include "qhostinfo_p.h"
+#include <private/qcore_symbian_p.h>
+#include <private/qsystemerror_p.h>
+#include <private/qnetworksession_p.h>
+#include <private/qhostaddress_p.h>
+
+// Header does not exist in the S60 5.0 SDK
+//#include <networking/dnd_err.h>
+const TInt KErrDndNameNotFound = -5120; // Returned when no data found for GetByName
+const TInt KErrDndAddrNotFound = -5121; // Returned when no data found for GetByAddr
+
+QT_BEGIN_NAMESPACE
+
+static void setError_helper(QHostInfo &info, TInt symbianError)
+{
+ switch (symbianError) {
+ case KErrDndNameNotFound:
+ case KErrDndAddrNotFound:
+ case KErrNotFound:
+ case KErrEof:
+ // various "no more results" error codes
+ info.setError(QHostInfo::HostNotFound);
+ info.setErrorString(QObject::tr("Host not found"));
+ break;
+ default:
+ // Unknown error
+ info.setError(QHostInfo::UnknownError);
+ info.setErrorString(QSystemError(symbianError, QSystemError::NativeError).toString());
+ break;
+ }
+}
+
+QHostInfo QHostInfoAgent::fromName(const QString &hostName, QSharedPointer<QNetworkSession> networkSession)
+{
+ QHostInfo results;
+
+ // Connect to ESOCK
+ RSocketServ socketServ(qt_symbianGetSocketServer());
+ RHostResolver hostResolver;
+
+
+ int err;
+ if (networkSession)
+ err = QNetworkSessionPrivate::nativeOpenHostResolver(*networkSession, hostResolver, KAfInet, KProtocolInetUdp);
+ else
+ err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp);
+ if (err) {
+ setError_helper(results, err);
+ return results;
+ }
+
+ TNameEntry nameResult;
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("QHostInfoAgent::fromName(%s) looking up...",
+ hostName.toLatin1().constData());
+#endif
+
+ QHostAddress address;
+ if (address.setAddress(hostName)) {
+ // Reverse lookup
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("(reverse lookup)");
+#endif
+ TInetAddr IpAdd;
+ IpAdd.Input(qt_QString2TPtrC(hostName));
+
+ // Synchronous request. nameResult returns Host Name.
+ err = hostResolver.GetByAddress(IpAdd, nameResult);
+ if (err) {
+ //for behavioural compatibility with Qt 4.7 and unix/windows
+ //backends: don't report error, return ip address as host name
+ results.setHostName(address.toString());
+ } else {
+ results.setHostName(qt_TDesC2QString(nameResult().iName));
+ }
+ results.setAddresses(QList<QHostAddress>() << address);
+ return results;
+ }
+
+ // IDN support
+ QByteArray aceHostname = QUrl::toAce(hostName);
+ results.setHostName(hostName);
+ if (aceHostname.isEmpty()) {
+ results.setError(QHostInfo::HostNotFound);
+ results.setErrorString(hostName.isEmpty() ?
+ QCoreApplication::translate("QHostInfoAgent", "No host name given") :
+ QCoreApplication::translate("QHostInfoAgent", "Invalid hostname"));
+ return results;
+ }
+
+
+ // Call RHostResolver::GetByAddress, and place all IPv4 addresses at the start and
+ // the IPv6 addresses at the end of the address list in results.
+
+ // Synchronous request.
+ err = hostResolver.GetByName(qt_QString2TPtrC(QString::fromLatin1(aceHostname)), nameResult);
+ if (err) {
+ setError_helper(results, err);
+ return results;
+ }
+
+ QList<QHostAddress> hostAddresses;
+
+ TInetAddr hostAdd = nameResult().iAddr;
+
+ if (!(nameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified()))
+ hostAddresses.append(qt_QHostAddressFromTInetAddr(hostAdd));
+
+ // Check if there's more than one IP address linkd to this name
+ while (hostResolver.Next(nameResult) == KErrNone) {
+ hostAdd = nameResult().iAddr;
+
+ // Ensure that record is valid (not an alias and with length greater than 0)
+ if (!(nameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified()))
+ hostAddresses.append(qt_QHostAddressFromTInetAddr(hostAdd));
+ }
+
+ hostResolver.Close();
+
+ results.setAddresses(hostAddresses);
+ return results;
+}
+
+QHostInfo QHostInfoAgent::fromName(const QString &hostName)
+{
+ // null shared pointer
+ QSharedPointer<QNetworkSession> networkSession;
+ return fromName(hostName, networkSession);
+}
+
+QString QHostInfo::localHostName()
+{
+ // Connect to ESOCK
+ RSocketServ socketServ(qt_symbianGetSocketServer());
+ RHostResolver hostResolver;
+
+ // RConnection not required to get the host name
+ int err = hostResolver.Open(socketServ, KAfInet, KProtocolInetUdp);
+ if (err)
+ return QString();
+
+ THostName hostName;
+ err = hostResolver.GetHostName(hostName);
+ if (err)
+ return QString();
+
+ hostResolver.Close();
+
+ return qt_TDesC2QString(hostName);
+}
+
+QString QHostInfo::localDomainName()
+{
+ // This concept does not exist on Symbian OS because the device can be on
+ // multiple networks with multiple "local domain" names.
+ // For now, return a null string.
+ return QString();
+}
+
+
+QSymbianHostResolver::QSymbianHostResolver(const QString &hostName, int identifier, QSharedPointer<QNetworkSession> networkSession)
+ : CActive(CActive::EPriorityStandard), iHostName(hostName),
+ iSocketServ(qt_symbianGetSocketServer()), iNetworkSession(networkSession), iResults(identifier)
+{
+ CActiveScheduler::Add(this);
+}
+
+QSymbianHostResolver::~QSymbianHostResolver()
+{
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostInfoLookupManager::~QSymbianHostResolver" << id();
+#endif
+ Cancel();
+ iHostResolver.Close();
+}
+
+// Async equivalent to QHostInfoAgent::fromName()
+void QSymbianHostResolver::requestHostLookup()
+{
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("QSymbianHostResolver::requestHostLookup(%s) looking up... (id = %d)",
+ iHostName.toLatin1().constData(), id());
+#endif
+
+ QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance();
+ if (manager->cache.isEnabled()) {
+ //check if name has been put in the cache while this request was queued
+ bool valid;
+ QHostInfo cachedResult = manager->cache.get(iHostName, &valid);
+ if (valid) {
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("...found in cache");
+#endif
+ iResults = cachedResult;
+ iState = ECompleteFromCache;
+ SetActive();
+ TRequestStatus* stat = &iStatus;
+ User::RequestComplete(stat, KErrNone);
+ return;
+ }
+ }
+
+ int err;
+ if (iNetworkSession) {
+ err = QNetworkSessionPrivate::nativeOpenHostResolver(*iNetworkSession, iHostResolver, KAfInet, KProtocolInetUdp);
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("using resolver from session (err = %d)", err);
+#endif
+ } else {
+ err = iHostResolver.Open(iSocketServ, KAfInet, KProtocolInetUdp);
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("using default resolver (err = %d)", err);
+#endif
+ }
+ if (err) {
+ setError_helper(iResults, err);
+ } else {
+
+ if (iAddress.setAddress(iHostName)) {
+ // Reverse lookup
+ IpAdd.Input(qt_QString2TPtrC(iHostName));
+
+ // Asynchronous request.
+ iHostResolver.GetByAddress(IpAdd, iNameResult, iStatus); // <---- ASYNC
+ iState = EGetByAddress;
+
+ } else {
+
+ // IDN support
+ QByteArray aceHostname = QUrl::toAce(iHostName);
+ iResults.setHostName(iHostName);
+ if (aceHostname.isEmpty()) {
+ iResults.setError(QHostInfo::HostNotFound);
+ iResults.setErrorString(iHostName.isEmpty() ?
+ QCoreApplication::translate("QHostInfoAgent", "No host name given") :
+ QCoreApplication::translate("QHostInfoAgent", "Invalid hostname"));
+
+ err = KErrArgument;
+ } else {
+ iEncodedHostName = QString::fromLatin1(aceHostname);
+ iHostNamePtr.Set(qt_QString2TPtrC(iEncodedHostName));
+
+ // Asynchronous request.
+ iHostResolver.GetByName(iHostNamePtr, iNameResult, iStatus);
+ iState = EGetByName;
+ }
+ }
+ }
+ SetActive();
+ if (err) {
+ iHostResolver.Close();
+
+ //self complete so that RunL can inform manager without causing recursion
+ iState = EError;
+ TRequestStatus* stat = &iStatus;
+ User::RequestComplete(stat, err);
+ }
+}
+
+void QSymbianHostResolver::abortHostLookup()
+{
+ if (resultEmitter.thread() == QThread::currentThread()) {
+#ifdef QHOSTINFO_DEBUG
+ qDebug("QSymbianHostResolver::abortHostLookup - deleting %d", id());
+#endif
+ //normal case, abort from same thread it was started
+ delete this; //will cancel outstanding request
+ } else {
+#ifdef QHOSTINFO_DEBUG
+ qDebug("QSymbianHostResolver::abortHostLookup - detaching %d", id());
+#endif
+ //abort from different thread, carry on but don't report the results
+ resultEmitter.disconnect();
+ }
+}
+
+void QSymbianHostResolver::DoCancel()
+{
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostResolver::DoCancel" << QThread::currentThreadId() << id() << (int)iState << this;
+#endif
+ if (iState == EGetByAddress || iState == EGetByName) {
+ //these states have made an async request to host resolver
+ iHostResolver.Cancel();
+ } else {
+ //for the self completing states there is nothing to cancel
+ Q_ASSERT(iState == EError || iState == ECompleteFromCache);
+ }
+}
+
+void QSymbianHostResolver::RunL()
+{
+ QT_TRYCATCH_LEAVING(run());
+}
+
+void QSymbianHostResolver::run()
+{
+ switch (iState) {
+ case EGetByName:
+ processNameResult();
+ break;
+ case EGetByAddress:
+ processAddressResult();
+ break;
+ case ECompleteFromCache:
+ case EError:
+ returnResults();
+ break;
+ default:
+ qWarning("QSymbianHostResolver internal error, bad state in run()");
+ iResults.setError(QHostInfo::UnknownError);
+ iResults.setErrorString(QSystemError(KErrCorrupt,QSystemError::NativeError).toString());
+ returnResults();
+ }
+}
+
+void QSymbianHostResolver::returnResults()
+{
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostResolver::returnResults" << iResults.error() << iResults.errorString();
+ foreach (QHostAddress addr, iResults.addresses())
+ qDebug() << addr;
+#endif
+ iState = EIdle;
+
+ QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance();
+ if (manager->cache.isEnabled()) {
+ manager->cache.put(iHostName, iResults);
+ }
+ manager->lookupFinished(this);
+
+ resultEmitter.emitResultsReady(iResults);
+
+ delete this;
+}
+
+TInt QSymbianHostResolver::RunError(TInt aError)
+{
+ QT_TRY {
+ iState = EIdle;
+
+ QSymbianHostInfoLookupManager *manager = QSymbianHostInfoLookupManager::globalInstance();
+ manager->lookupFinished(this);
+
+ setError_helper(iResults, aError);
+
+ resultEmitter.emitResultsReady(iResults);
+ }
+ QT_CATCH(...) {}
+
+ delete this;
+
+ return KErrNone;
+}
+
+void QSymbianHostResolver::processNameResult()
+{
+ if (iStatus.Int() == KErrNone) {
+ TInetAddr hostAdd = iNameResult().iAddr;
+
+ // Ensure that record is valid (not an alias and with length greater than 0)
+ if (!(iNameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified())) {
+ iHostAddresses.append(qt_QHostAddressFromTInetAddr(hostAdd));
+ }
+
+ iState = EGetByName;
+ iHostResolver.Next(iNameResult, iStatus);
+ SetActive();
+ }
+ else {
+ // No more addresses, so return the results (or an error if there aren't any).
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostResolver::processNameResult with err=" << iStatus.Int() << "count=" << iHostAddresses.count();
+#endif
+ if (iHostAddresses.count() > 0) {
+ iResults.setAddresses(iHostAddresses);
+ } else {
+ iState = EError;
+ setError_helper(iResults, iStatus.Int());
+ }
+ returnResults();
+ }
+}
+
+void QSymbianHostResolver::processAddressResult()
+{
+ TInt err = iStatus.Int();
+
+ if (err < 0) {
+ //For behavioural compatibility with Qt 4.7, don't report errors on reverse lookup,
+ //return the address as a string (same as unix/windows backends)
+ iResults.setHostName(iAddress.toString());
+ } else {
+ iResults.setHostName(qt_TDesC2QString(iNameResult().iName));
+ }
+ iResults.setAddresses(QList<QHostAddress>() << iAddress);
+ returnResults();
+}
+
+
+int QSymbianHostResolver::id()
+{
+ return iResults.lookupId();
+}
+
+QSymbianHostInfoLookupManager::QSymbianHostInfoLookupManager()
+{
+}
+
+QSymbianHostInfoLookupManager::~QSymbianHostInfoLookupManager()
+{
+}
+
+void QSymbianHostInfoLookupManager::clear()
+{
+ QMutexLocker locker(&mutex);
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostInfoLookupManager::clear" << QThread::currentThreadId();
+#endif
+ foreach (QSymbianHostResolver *hr, iCurrentLookups)
+ hr->abortHostLookup();
+ iCurrentLookups.clear();
+ qDeleteAll(iScheduledLookups);
+ cache.clear();
+}
+
+void QSymbianHostInfoLookupManager::lookupFinished(QSymbianHostResolver *r)
+{
+ QMutexLocker locker(&mutex);
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostInfoLookupManager::lookupFinished" << QThread::currentThreadId() << r->id() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
+#endif
+ // remove finished lookup from array and destroy
+ TInt count = iCurrentLookups.count();
+ for (TInt i = 0; i < count; i++) {
+ if (iCurrentLookups[i]->id() == r->id()) {
+ iCurrentLookups.removeAt(i);
+ break;
+ }
+ }
+
+ runNextLookup();
+}
+
+void QSymbianHostInfoLookupManager::runNextLookup()
+{
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostInfoLookupManager::runNextLookup" << QThread::currentThreadId() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
+#endif
+ // check to see if there are any scheduled lookups
+ for (int i=0; i<iScheduledLookups.count(); i++) {
+ QSymbianHostResolver* hostResolver = iScheduledLookups.at(i);
+ if (hostResolver->resultEmitter.thread() == QThread::currentThread()) {
+ // if so, move one to the current lookups and run it
+ iCurrentLookups.append(hostResolver);
+ iScheduledLookups.removeAt(i);
+ hostResolver->requestHostLookup();
+ // if spare capacity, try to start another one
+ if (iCurrentLookups.count() >= KMaxConcurrentLookups)
+ break;
+ i--; //compensate for removeAt
+ }
+ }
+}
+
+// called from QHostInfo
+void QSymbianHostInfoLookupManager::scheduleLookup(QSymbianHostResolver* r)
+{
+ QMutexLocker locker(&mutex);
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostInfoLookupManager::scheduleLookup" << QThread::currentThreadId() << r->id() << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
+#endif
+ // Check to see if we have space on the current lookups pool.
+ bool defer = false;
+ if (iCurrentLookups.count() >= KMaxConcurrentLookups) {
+ // busy, defer unless there are no request in this thread
+ // at least one active request per thread with queued requests is needed
+ for (int i=0; i < iCurrentLookups.count();i++) {
+ if (iCurrentLookups.at(i)->resultEmitter.thread() == QThread::currentThread()) {
+ defer = true;
+ break;
+ }
+ }
+ }
+ if (defer) {
+ // If no, schedule for later.
+ iScheduledLookups.append(r);
+#if defined(QHOSTINFO_DEBUG)
+ qDebug(" - scheduled");
+#endif
+ return;
+ } else {
+ // If yes, add it to the current lookups.
+ iCurrentLookups.append(r);
+
+ // ... and trigger the async call.
+ r->requestHostLookup();
+ }
+}
+
+void QSymbianHostInfoLookupManager::abortLookup(int id)
+{
+ QMutexLocker locker(&mutex);
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug() << "QSymbianHostInfoLookupManager::abortLookup" << QThread::currentThreadId() << id << "current" << iCurrentLookups.count() << "queued" << iScheduledLookups.count();
+#endif
+ int i = 0;
+ // Find the aborted lookup by ID.
+ // First in the current lookups.
+ for (i = 0; i < iCurrentLookups.count(); i++) {
+ if (id == iCurrentLookups[i]->id()) {
+ QSymbianHostResolver* r = iCurrentLookups.at(i);
+ iCurrentLookups.removeAt(i);
+ r->abortHostLookup();
+ runNextLookup();
+ return;
+ }
+ }
+ // Then in the scheduled lookups.
+ for (i = 0; i < iScheduledLookups.count(); i++) {
+ if (id == iScheduledLookups[i]->id()) {
+ QSymbianHostResolver* r = iScheduledLookups.at(i);
+ iScheduledLookups.removeAt(i);
+ delete r;
+ return;
+ }
+ }
+}
+
+QSymbianHostInfoLookupManager* QSymbianHostInfoLookupManager::globalInstance()
+{
+ return static_cast<QSymbianHostInfoLookupManager*>
+ (QAbstractHostInfoLookupManager::globalInstance());
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index 2a993b7..0a56bc3 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -132,9 +132,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
// Load res_init on demand.
static volatile bool triedResolve = false;
if (!triedResolve) {
-#ifndef QT_NO_THREAD
QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_init));
-#endif
if (!triedResolve) {
resolveLibrary();
triedResolve = true;
@@ -149,7 +147,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
if (address.setAddress(hostName)) {
// Reverse lookup
// Reverse lookups using getnameinfo are broken on darwin, use gethostbyaddr instead.
-#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN) && !defined (Q_OS_SYMBIAN)
+#if !defined (QT_NO_GETADDRINFO) && !defined (Q_OS_DARWIN)
sockaddr_in sa4;
#ifndef QT_NO_IPV6
sockaddr_in6 sa6;
@@ -210,23 +208,12 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
#ifdef Q_ADDRCONFIG
hints.ai_flags = Q_ADDRCONFIG;
#endif
-#ifdef Q_OS_SYMBIAN
-# ifdef QHOSTINFO_DEBUG
- qDebug() << "Setting flags: 'hints.ai_flags &= AI_V4MAPPED | AI_ALL'";
-# endif
-#endif
int result = getaddrinfo(aceHostname.constData(), 0, &hints, &res);
# ifdef Q_ADDRCONFIG
if (result == EAI_BADFLAGS) {
// if the lookup failed with AI_ADDRCONFIG set, try again without it
hints.ai_flags = 0;
-#ifdef Q_OS_SYMBIAN
-# ifdef QHOSTINFO_DEBUG
- qDebug() << "Setting flags: 'hints.ai_flags &= AI_V4MAPPED | AI_ALL'";
-# endif
- hints.ai_flags &= AI_V4MAPPED | AI_ALL;
-#endif
result = getaddrinfo(aceHostname.constData(), 0, &hints, &res);
}
# endif
diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp
index 73bf222..6fc5b7b 100644
--- a/src/network/kernel/qhostinfo_win.cpp
+++ b/src/network/kernel/qhostinfo_win.cpp
@@ -39,11 +39,6 @@
**
****************************************************************************/
-#if defined Q_CC_MSVC && _MSC_VER <=1300
-//VC.net 2002 support for templates doesn't match some PSDK requirements
-#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
-#endif
-
#include <winsock2.h>
#include "qhostinfo_p.h"
@@ -115,9 +110,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
// Load res_init on demand.
static volatile bool triedResolve = false;
if (!triedResolve) {
-#ifndef QT_NO_THREAD
QMutexLocker locker(QMutexPool::globalInstanceGet(&local_getaddrinfo));
-#endif
if (!triedResolve) {
resolveLibrary();
triedResolve = true;
diff --git a/src/network/kernel/qnetworkinterface_symbian.cpp b/src/network/kernel/qnetworkinterface_symbian.cpp
index dfdcb01..dca6cf4 100644
--- a/src/network/kernel/qnetworkinterface_symbian.cpp
+++ b/src/network/kernel/qnetworkinterface_symbian.cpp
@@ -67,22 +67,27 @@ static QNetworkInterface::InterfaceFlags convertFlags(const TSoInetInterfaceInfo
return flags;
}
+QHostAddress qt_QHostAddressFromTInetAddr(const TInetAddr& addr)
+{
+ if (addr.IsV4Mapped() || addr.Family() == KAfInet) {
+ //convert v4 host address
+ return QHostAddress(addr.Address());
+ } else {
+ //convert v6 host address
+ return QHostAddress((quint8 *)(addr.Ip6Address().u.iAddr8));
+ }
+}
+
static QList<QNetworkInterfacePrivate *> interfaceListing()
{
TInt err(KErrNone);
QList<QNetworkInterfacePrivate *> interfaces;
-
- // Connect to Native socket server
- RSocketServ socketServ;
- err = socketServ.Connect();
- if (err)
- return interfaces;
+ QList<QHostAddress> addressesWithEstimatedNetmasks;
// Open dummy socket for interface queries
RSocket socket;
- err = socket.Open(socketServ, _L("udp"));
+ err = socket.Open(qt_symbianGetSocketServer(), _L("udp"));
if (err) {
- socketServ.Close();
return interfaces;
}
@@ -90,7 +95,6 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
err = socket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl);
if (err) {
socket.Close();
- socketServ.Close();
return interfaces;
}
@@ -98,8 +102,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
TPckgBuf<TSoInetInterfaceInfo> infoPckg;
TSoInetInterfaceInfo &info = infoPckg();
while (socket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, infoPckg) == KErrNone) {
- // Do not include IPv6 addresses because netmask and broadcast address cannot be determined correctly
- if (info.iName != KNullDesC && info.iAddress.IsV4Mapped()) {
+ if (info.iName != KNullDesC) {
TName address;
QNetworkAddressEntry entry;
QNetworkInterfacePrivate *iface = 0;
@@ -121,40 +124,58 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
}
// Get the address of the interface
- info.iAddress.Output(address);
- entry.setIp(QHostAddress(qt_TDesC2QString(address)));
+ entry.setIp(qt_QHostAddressFromTInetAddr(info.iAddress));
+
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "address is" << info.iAddress.Family() << entry.ip();
+ qDebug() << "netmask is" << info.iNetMask.Family() << qt_QHostAddressFromTInetAddr( info.iNetMask );
+#endif
// Get the interface netmask
- // For some reason netmask is always 0.0.0.0
- // info.iNetMask.Output(address);
- // entry.setNetmask( QHostAddress( qt_TDesC2QString( address ) ) );
-
- // Workaround: Let Symbian determine netmask based on IP address class
- // TODO: Works only for IPv4 - Task: 259128 Implement IPv6 support
- TInetAddr netmask;
- netmask.NetMask(info.iAddress);
- netmask.Output(address);
- entry.setNetmask(QHostAddress(qt_TDesC2QString(address)));
-
- // Get the interface broadcast address
- if (iface->flags & QNetworkInterface::CanBroadcast) {
- // For some reason broadcast address is always 0.0.0.0
- // info.iBrdAddr.Output(address);
- // entry.setBroadcast( QHostAddress( qt_TDesC2QString( address ) ) );
-
- // Workaround: Let Symbian determine broadcast address based on IP address
- // TODO: Works only for IPv4 - Task: 259128 Implement IPv6 support
- TInetAddr broadcast;
- broadcast.NetBroadcast(info.iAddress);
- broadcast.Output(address);
- entry.setBroadcast(QHostAddress(qt_TDesC2QString(address)));
+ if (info.iNetMask.IsUnspecified()) {
+ // For some reason netmask is always 0.0.0.0 for IPv4 interfaces
+ // and loopback interfaces (which we statically know)
+ if (info.iAddress.IsV4Mapped()) {
+ if (info.iFeatures & KIfIsLoopback) {
+ entry.setPrefixLength(32);
+ } else {
+ // Workaround: Let Symbian determine netmask based on IP address class (IPv4 only API)
+ TInetAddr netmask;
+ netmask.NetMask(info.iAddress);
+ entry.setNetmask(QHostAddress(netmask.Address())); //binary convert v4 address
+ addressesWithEstimatedNetmasks << entry.ip();
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "address class determined netmask" << entry.netmask();
+#endif
+ }
+ } else {
+ // For IPv6 interfaces
+ if (info.iFeatures & KIfIsLoopback) {
+ entry.setPrefixLength(128);
+ } else if (info.iNetMask.IsUnspecified()) {
+ //Don't see this error for IPv6, but try to handle it if it happens
+ entry.setPrefixLength(64); //most common
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "total guess netmask" << entry.netmask();
+#endif
+ addressesWithEstimatedNetmasks << entry.ip();
+ }
+ }
+ } else {
+ //Expected code path for IPv6 non loopback interfaces (IPv4 could come here if symbian is fixed)
+ entry.setNetmask(qt_QHostAddressFromTInetAddr(info.iNetMask));
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "reported netmask" << entry.netmask();
+#endif
}
+ // broadcast address is determined from the netmask in postProcess()
+
// Add new entry to interface address entries
iface->addressEntries << entry;
#if defined(QNETWORKINTERFACE_DEBUG)
- printf("\n Found network interface %s, interface flags:\n\
+ qDebug("\n Found network interface %s, interface flags:\n\
IsUp = %d, IsRunning = %d, CanBroadcast = %d,\n\
IsLoopBack = %d, IsPointToPoint = %d, CanMulticast = %d, \n\
ip = %s, netmask = %s, broadcast = %s,\n\
@@ -168,15 +189,20 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
}
}
+ // if we didn't have to guess any netmasks, then we're done.
+ if (addressesWithEstimatedNetmasks.isEmpty()) {
+ socket.Close();
+ return interfaces;
+ }
+
// we will try to use routing info to detect more precisely
- // netmask and then ::postProcess() should calculate
+ // estimated netmasks and then ::postProcess() should calculate
// broadcast addresses
// use dummy socket to start enumerating routes
err = socket.SetOpt(KSoInetEnumRoutes, KSolInetRtCtrl);
if (err) {
socket.Close();
- socketServ.Close();
// return what we have
// up to this moment
return interfaces;
@@ -185,16 +211,21 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
TSoInetRouteInfo routeInfo;
TPckg<TSoInetRouteInfo> routeInfoPkg(routeInfo);
while (socket.GetOpt(KSoInetNextRoute, KSolInetRtCtrl, routeInfoPkg) == KErrNone) {
- TName address;
-
// get interface address
- routeInfo.iIfAddr.Output(address);
- QHostAddress ifAddr(qt_TDesC2QString(address));
+ QHostAddress ifAddr(qt_QHostAddressFromTInetAddr(routeInfo.iIfAddr));
if (ifAddr.isNull())
continue;
+ if (!addressesWithEstimatedNetmasks.contains(ifAddr)) {
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "skipping route from" << ifAddr << "because it wasn't an estimated netmask";
+#endif
+ continue;
+ }
- routeInfo.iDstAddr.Output(address);
- QHostAddress destination(qt_TDesC2QString(address));
+ QHostAddress destination(qt_QHostAddressFromTInetAddr(routeInfo.iDstAddr));
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "route from" << ifAddr << "to" << destination;
+#endif
if (destination.isNull() || destination != ifAddr)
continue;
@@ -205,17 +236,13 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
QNetworkAddressEntry entry = iface->addressEntries.at(eindex);
if (entry.ip() != ifAddr) {
continue;
- } else if (entry.ip().protocol() != QAbstractSocket::IPv4Protocol) {
- // skip if not IPv4 address (e.g. IPv6)
- // as results not reliable on Symbian
- continue;
- } else {
- routeInfo.iNetMask.Output(address);
- QHostAddress netmask(qt_TDesC2QString(address));
+ } else if (!routeInfo.iNetMask.IsUnspecified()) {
+ //the route may also return 0.0.0.0 netmask, in which case don't use it.
+ QHostAddress netmask(qt_QHostAddressFromTInetAddr(routeInfo.iNetMask));
entry.setNetmask(netmask);
- // NULL boradcast address for
- // ::postProcess to have effect
- entry.setBroadcast(QHostAddress());
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << " - route netmask" << routeInfo.iNetMask.Family() << netmask << " (using route determined netmask)";
+#endif
iface->addressEntries.replace(eindex, entry);
}
}
@@ -223,7 +250,6 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
}
socket.Close();
- socketServ.Close();
return interfaces;
}
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index d3e4c94..71d61a4 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -228,6 +228,10 @@
#include "qmutex.h"
#include "qurl.h"
+#ifndef QT_NO_BEARERMANAGEMENT
+#include <QtNetwork/QNetworkConfiguration>
+#endif
+
QT_BEGIN_NAMESPACE
class QSocks5SocketEngineHandler;
@@ -716,6 +720,9 @@ public:
QUrl remote;
int localPort;
QNetworkProxyQuery::QueryType type;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkConfiguration config;
+#endif
};
template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
@@ -777,6 +784,11 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
like choosing an caching HTTP proxy for HTTP-based connections,
but a more powerful SOCKSv5 proxy for all others.
+ The network configuration specifies which configuration to use,
+ when bearer management is used. For example on a mobile phone
+ the proxy settings are likely to be different for the cellular
+ network vs WLAN.
+
Some of the criteria may not make sense in all of the types of
query. The following table lists the criteria that are most
commonly used, according to the type of query.
@@ -902,6 +914,74 @@ QNetworkProxyQuery::QNetworkProxyQuery(quint16 bindPort, const QString &protocol
d->type = queryType;
}
+#ifndef QT_NO_BEARERMANAGEMENT
+/*!
+ \since 4.8
+
+ Constructs a QNetworkProxyQuery with the URL \a requestUrl and
+ sets the query type to \a queryType. The specified \a networkConfiguration
+ is used to resolve the proxy settings.
+
+ \sa protocolTag(), peerHostName(), peerPort(), networkConfiguration()
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
+ const QUrl &requestUrl, QueryType queryType)
+{
+ d->config = networkConfiguration;
+ d->remote = requestUrl;
+ d->type = queryType;
+}
+
+/*!
+ \since 4.8
+
+ Constructs a QNetworkProxyQuery of type \a queryType and sets the
+ protocol tag to be \a protocolTag. This constructor is suitable
+ for QNetworkProxyQuery::TcpSocket queries, because it sets the
+ peer hostname to \a hostname and the peer's port number to \a
+ port. The specified \a networkConfiguration
+ is used to resolve the proxy settings.
+
+ \sa networkConfiguration()
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
+ const QString &hostname, int port,
+ const QString &protocolTag,
+ QueryType queryType)
+{
+ d->config = networkConfiguration;
+ d->remote.setScheme(protocolTag);
+ d->remote.setHost(hostname);
+ d->remote.setPort(port);
+ d->type = queryType;
+}
+
+/*!
+ \since 4.8
+
+ Constructs a QNetworkProxyQuery of type \a queryType and sets the
+ protocol tag to be \a protocolTag. This constructor is suitable
+ for QNetworkProxyQuery::TcpSocket queries because it sets the
+ local port number to \a bindPort. The specified \a networkConfiguration
+ is used to resolve the proxy settings.
+
+ Note that \a bindPort is of type quint16 to indicate the exact
+ port number that is requested. The value of -1 (unknown) is not
+ allowed in this context.
+
+ \sa localPort(), networkConfiguration()
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
+ quint16 bindPort, const QString &protocolTag,
+ QueryType queryType)
+{
+ d->config = networkConfiguration;
+ d->remote.setScheme(protocolTag);
+ d->localPort = bindPort;
+ d->type = queryType;
+}
+#endif
+
/*!
Constructs a QNetworkProxyQuery object that is a copy of \a other.
*/
@@ -1116,6 +1196,37 @@ void QNetworkProxyQuery::setUrl(const QUrl &url)
d->remote = url;
}
+#ifndef QT_NO_BEARERMANAGEMENT
+/*!
+ Returns the network configuration of the proxy query.
+
+ \sa setNetworkConfiguration()
+*/
+QNetworkConfiguration QNetworkProxyQuery::networkConfiguration() const
+{
+ return d ? d->config : QNetworkConfiguration();
+}
+
+/*!
+ \since 4.8
+
+ Sets the network configuration component of this QNetworkProxyQuery
+ object to be \a networkConfiguration. The network configuration can
+ be used to return different proxy settings based on the network in
+ use, for example WLAN vs cellular networks on a mobile phone.
+
+ In the case of "user choice" or "service network" configurations,
+ you should first start the QNetworkSession and obtain the active
+ configuration from its properties.
+
+ \sa networkConfiguration()
+*/
+void QNetworkProxyQuery::setNetworkConfiguration(const QNetworkConfiguration &networkConfiguration)
+{
+ d->config = networkConfiguration;
+}
+#endif
+
/*!
\class QNetworkProxyFactory
\brief The QNetworkProxyFactory class provides fine-grained proxy selection.
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index fd8b3a8..3ad0522 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -54,6 +54,7 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Network)
class QUrl;
+class QNetworkConfiguration;
class QNetworkProxyQueryPrivate;
class Q_NETWORK_EXPORT QNetworkProxyQuery
@@ -73,6 +74,16 @@ public:
QNetworkProxyQuery(quint16 bindPort, const QString &protocolTag = QString(),
QueryType queryType = TcpServer);
QNetworkProxyQuery(const QNetworkProxyQuery &other);
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
+ const QUrl &requestUrl, QueryType queryType = UrlRequest);
+ QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
+ const QString &hostname, int port, const QString &protocolTag = QString(),
+ QueryType queryType = TcpSocket);
+ QNetworkProxyQuery(const QNetworkConfiguration &networkConfiguration,
+ quint16 bindPort, const QString &protocolTag = QString(),
+ QueryType queryType = TcpServer);
+#endif
~QNetworkProxyQuery();
QNetworkProxyQuery &operator=(const QNetworkProxyQuery &other);
bool operator==(const QNetworkProxyQuery &other) const;
@@ -97,6 +108,11 @@ public:
QUrl url() const;
void setUrl(const QUrl &url);
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkConfiguration networkConfiguration() const;
+ void setNetworkConfiguration(const QNetworkConfiguration &networkConfiguration);
+#endif
+
private:
QSharedDataPointer<QNetworkProxyQueryPrivate> d;
};
diff --git a/src/network/kernel/qnetworkproxy_symbian.cpp b/src/network/kernel/qnetworkproxy_symbian.cpp
index f706a51..73068d6 100644
--- a/src/network/kernel/qnetworkproxy_symbian.cpp
+++ b/src/network/kernel/qnetworkproxy_symbian.cpp
@@ -58,6 +58,7 @@
#include <commsdattypeinfov1_1.h> // CCDIAPRecord, CCDProxiesRecord
#include <commsdattypesv1_1.h> // KCDTIdIAPRecord, KCDTIdProxiesRecord
#include <QtNetwork/QNetworkConfigurationManager>
+#include <QtNetwork/QNetworkConfiguration>
#include <QFlags>
using namespace CommsDat;
@@ -67,82 +68,96 @@ QT_BEGIN_NAMESPACE
class SymbianIapId
{
public:
- enum State{
- NotValid,
- Valid
- };
- Q_DECLARE_FLAGS(States, State)
- SymbianIapId() {}
+ SymbianIapId() : valid(false), id(0) {}
~SymbianIapId() {}
- void setIapId(TUint32 iapId) { iapState |= Valid; id = iapId; }
- bool isValid() { return iapState == Valid; }
+ void setIapId(TUint32 iapId) { valid = true; id = iapId; }
+ bool isValid() { return valid; }
TUint32 iapId() { return id; }
private:
- QFlags<States> iapState;
+ bool valid;
TUint32 id;
};
-Q_DECLARE_OPERATORS_FOR_FLAGS(SymbianIapId::States)
-
class SymbianProxyQuery
{
public:
static QNetworkConfiguration findCurrentConfiguration(QNetworkConfigurationManager& configurationManager);
- static SymbianIapId getIapId(QNetworkConfigurationManager& configurationManager);
+ static QNetworkConfiguration findCurrentConfigurationFromServiceNetwork(const QNetworkConfiguration& serviceNetwork);
+ static SymbianIapId getIapId(QNetworkConfigurationManager &configurationManager, const QNetworkProxyQuery &query);
static CCDIAPRecord *getIapRecordLC(TUint32 aIAPId, CMDBSession &aDb);
static CMDBRecordSet<CCDProxiesRecord> *prepareQueryLC(TUint32 serviceId, TDesC& serviceType);
static QList<QNetworkProxy> proxyQueryL(TUint32 aIAPId, const QNetworkProxyQuery &query);
};
+QNetworkConfiguration SymbianProxyQuery::findCurrentConfigurationFromServiceNetwork(const QNetworkConfiguration& serviceNetwork)
+{
+ // Note: This code assumes that the only unambigious way to
+ // find current proxy config is if there is only one access point
+ // or if the found access point is immediately usable.
+ QList<QNetworkConfiguration> childConfigurations = serviceNetwork.children();
+ if (childConfigurations.isEmpty()) {
+ qWarning("QNetworkProxyFactory::systemProxyForQuery called with empty service network");
+ return QNetworkConfiguration();
+ } else if (childConfigurations.count() == 1) {
+ //if only one IAP in the service network, it's always going to be used.
+ return childConfigurations.at(0);
+ } else {
+ //use highest priority active config, if available
+ for (int index = 0; index < childConfigurations.count(); index++) {
+ QNetworkConfiguration childConfig = childConfigurations.at(index);
+ if (childConfig.isValid() && childConfig.state() == QNetworkConfiguration::Active)
+ return childConfig;
+ }
+ //otherwise use highest priority discovered config (that's the one which will be activated if start were called now)
+ for (int index = 0; index < childConfigurations.count(); index++) {
+ QNetworkConfiguration childConfig = childConfigurations.at(index);
+ if (childConfig.isValid() && childConfig.state() == QNetworkConfiguration::Discovered)
+ return childConfig;
+ }
+ //otherwise the highest priority defined (most likely to be activated if all were available when start is called)
+ qWarning("QNetworkProxyFactory::systemProxyForQuery called with service network, but none of its IAPs are available");
+ return childConfigurations.at(0);
+ }
+}
+
QNetworkConfiguration SymbianProxyQuery::findCurrentConfiguration(QNetworkConfigurationManager& configurationManager)
{
QList<QNetworkConfiguration> activeConfigurations = configurationManager.allConfigurations(
QNetworkConfiguration::Active);
- QNetworkConfiguration currentConfig;
if (activeConfigurations.count() > 0) {
- currentConfig = activeConfigurations.at(0);
+ return activeConfigurations.at(0);
} else {
// No active configurations, try default one
QNetworkConfiguration defaultConfiguration = configurationManager.defaultConfiguration();
if (defaultConfiguration.isValid()) {
switch (defaultConfiguration.type()) {
case QNetworkConfiguration::InternetAccessPoint:
- currentConfig = defaultConfiguration;
- break;
+ return defaultConfiguration;
case QNetworkConfiguration::ServiceNetwork:
- {
- // Note: This code assumes that the only unambigious way to
- // find current proxy config is if there is only one access point
- // or if the found access point is immediately usable.
- QList<QNetworkConfiguration> childConfigurations = defaultConfiguration.children();
- if (childConfigurations.count() == 1) {
- currentConfig = childConfigurations.at(0);
- } else {
- for (int index = 0; index < childConfigurations.count(); index++) {
- QNetworkConfiguration childConfig = childConfigurations.at(index);
- if (childConfig.isValid() && childConfig.state() == QNetworkConfiguration::Discovered) {
- currentConfig = childConfig;
- break;
- }
- }
- }
- }
- break;
+ return findCurrentConfigurationFromServiceNetwork(defaultConfiguration);
case QNetworkConfiguration::UserChoice:
- // User choice is not a valid configuration for proxy discovery
+ qWarning("QNetworkProxyFactory::systemProxyForQuery called with user choice configuration, which is not valid");
break;
}
}
}
- return currentConfig;
+ return QNetworkConfiguration();
}
-SymbianIapId SymbianProxyQuery::getIapId(QNetworkConfigurationManager& configurationManager)
+SymbianIapId SymbianProxyQuery::getIapId(QNetworkConfigurationManager& configurationManager, const QNetworkProxyQuery &query)
{
SymbianIapId iapId;
- QNetworkConfiguration currentConfig = findCurrentConfiguration(configurationManager);
- if (currentConfig.isValid()) {
+ QNetworkConfiguration currentConfig = query.networkConfiguration();
+ if (!currentConfig.isValid()) {
+ //If config is not specified, then try to find out an active or default one
+ currentConfig = findCurrentConfiguration(configurationManager);
+ }
+ if (currentConfig.isValid() && currentConfig.type() == QNetworkConfiguration::ServiceNetwork) {
+ //convert service network to the real IAP.
+ currentConfig = findCurrentConfigurationFromServiceNetwork(currentConfig);
+ }
+ if (currentConfig.isValid() && currentConfig.type() == QNetworkConfiguration::InternetAccessPoint) {
// Note: the following code assumes that the identifier is in format
// I_xxxx where xxxx is the identifier of IAP. This is meant as a
// temporary solution until there is a support for returning
@@ -231,7 +246,12 @@ QList<QNetworkProxy> SymbianProxyQuery::proxyQueryL(TUint32 aIAPId, const QNetwo
CleanupStack::Pop(); // serverName
TUint32 port = proxyRecord->iPortNumber;
- QNetworkProxy proxy(QNetworkProxy::HttpProxy, serverNameQt, port);
+ //Symbian config doesn't include proxy type, assume http unless the port matches assigned port of another type
+ //Mobile operators use a wide variety of port numbers for http proxies.
+ QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy;
+ if (port == 1080) //IANA assigned port for SOCKS
+ proxyType = QNetworkProxy::Socks5Proxy;
+ QNetworkProxy proxy(proxyType, serverNameQt, port);
foundProxies.append(proxy);
}
}
@@ -249,7 +269,7 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
SymbianIapId iapId;
TInt error;
QNetworkConfigurationManager manager;
- iapId = SymbianProxyQuery::getIapId(manager);
+ iapId = SymbianProxyQuery::getIapId(manager, query);
if (iapId.isValid()) {
TRAP(error, proxies = SymbianProxyQuery::proxyQueryL(iapId.iapId(), query))
if (error != KErrNone) {
diff --git a/src/network/network.pro b/src/network/network.pro
index 7ed7d3a..948922b 100644
--- a/src/network/network.pro
+++ b/src/network/network.pro
@@ -13,7 +13,7 @@ DEFINES += QT_BUILD_NETWORK_LIB QT_NO_USING_NAMESPACE
QT = core
win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x64000000
-unix:QMAKE_PKGCONFIG_REQUIRES = QtCore
+unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore
include(../qbase.pri)
include(access/access.pri)
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index bd4c9e4..5316626 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -356,6 +356,10 @@
to enable.
\value KeepAliveOption Set this to 1 to enable the SO_KEEPALIVE socket option
+ \value MulticastTtlOption Set this to an integer value to set IP_MULTICAST_TTL (TTL for multicast datagrams) socket option.
+
+ \value MulticastLoopbackOption Set this to 1 to enable the IP_MULTICAST_LOOP (multicast loopback) socket option.
+
\sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption()
*/
@@ -363,6 +367,7 @@
#include "qabstractsocket_p.h"
#include "private/qhostinfo_p.h"
+#include "private/qnetworksession_p.h"
#include <qabstracteventdispatcher.h>
#include <qhostaddress.h>
@@ -371,6 +376,7 @@
#include <qpointer.h>
#include <qtimer.h>
#include <qelapsedtimer.h>
+#include <qscopedvaluerollback.h>
#ifndef QT_NO_OPENSSL
#include <QtNetwork/qsslsocket.h>
@@ -466,9 +472,6 @@ QAbstractSocketPrivate::QAbstractSocketPrivate()
peerPort(0),
socketEngine(0),
cachedSocketDescriptor(-1),
-#ifdef Q_OS_LINUX
- addToBytesAvailable(0),
-#endif
readBufferMaxSize(0),
readBuffer(QABSTRACTSOCKET_BUFFERSIZE),
writeBuffer(QABSTRACTSOCKET_BUFFERSIZE),
@@ -549,6 +552,10 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc
q->setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));
return false;
}
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket engine (if it has been set)
+ socketEngine->setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
#ifndef QT_NO_NETWORKPROXY
//copy user agent to socket engine (if it has been set)
socketEngine->setProperty("_q_user-agent", q->property("_q_user-agent"));
@@ -595,6 +602,7 @@ bool QAbstractSocketPrivate::canReadNotification()
socketEngine->setReadNotificationEnabled(false);
}
}
+ QScopedValueRollback<bool> rsncrollback(readSocketNotifierCalled);
readSocketNotifierCalled = true;
if (!isBuffered)
@@ -608,7 +616,6 @@ bool QAbstractSocketPrivate::canReadNotification()
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full");
#endif
- readSocketNotifierCalled = false;
return false;
}
@@ -620,7 +627,6 @@ bool QAbstractSocketPrivate::canReadNotification()
qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket");
#endif
q->disconnectFromHost();
- readSocketNotifierCalled = false;
return false;
}
newBytes = readBuffer.size() - newBytes;
@@ -634,14 +640,15 @@ bool QAbstractSocketPrivate::canReadNotification()
// only emit readyRead() when not recursing, and only if there is data available
bool hasData = newBytes > 0
#ifndef QT_NO_UDPSOCKET
- || (!isBuffered && socketEngine && socketEngine->hasPendingDatagrams())
+ || (!isBuffered && socketType != QAbstractSocket::TcpSocket && socketEngine && socketEngine->hasPendingDatagrams())
#endif
+ || (!isBuffered && socketType == QAbstractSocket::TcpSocket && socketEngine)
;
if (!emittedReadyRead && hasData) {
+ QScopedValueRollback<bool> r(emittedReadyRead);
emittedReadyRead = true;
emit q->readyRead();
- emittedReadyRead = false;
}
// If we were closed as a result of the readyRead() signal,
@@ -650,7 +657,6 @@ bool QAbstractSocketPrivate::canReadNotification()
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning");
#endif
- readSocketNotifierCalled = false;
return true;
}
@@ -664,7 +670,6 @@ bool QAbstractSocketPrivate::canReadNotification()
socketEngine->setReadNotificationEnabled(readSocketNotifierState);
readSocketNotifierStateSet = false;
}
- readSocketNotifierCalled = false;
return true;
}
@@ -751,11 +756,11 @@ bool QAbstractSocketPrivate::flush()
if (written < 0) {
socketError = socketEngine->error();
q->setErrorString(socketEngine->errorString());
- emit q->error(socketError);
- // an unexpected error so close the socket.
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug() << "QAbstractSocketPrivate::flush() write error, aborting." << socketEngine->errorString();
#endif
+ emit q->error(socketError);
+ // an unexpected error so close the socket.
q->abort();
return false;
}
@@ -770,9 +775,9 @@ bool QAbstractSocketPrivate::flush()
if (written > 0) {
// Don't emit bytesWritten() recursively.
if (!emittedBytesWritten) {
+ QScopedValueRollback<bool> r(emittedBytesWritten);
emittedBytesWritten = true;
emit q->bytesWritten(written);
- emittedBytesWritten = false;
}
}
@@ -1136,10 +1141,6 @@ bool QAbstractSocketPrivate::readFromSocket()
Q_Q(QAbstractSocket);
// Find how many bytes we can read from the socket layer.
qint64 bytesToRead = socketEngine->bytesAvailable();
-#ifdef Q_OS_LINUX
- if (bytesToRead > 0) // ### See setSocketDescriptor()
- bytesToRead += addToBytesAvailable;
-#endif
if (bytesToRead == 0) {
// Under heavy load, certain conditions can trigger read notifications
// for socket notifiers on which there is no activity. If we continue
@@ -1374,10 +1375,6 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint
d->localAddress.clear();
d->peerAddress.clear();
d->peerName = hostName;
-#ifdef Q_OS_LINUX
- // ### See setSocketDescriptor().
- d->addToBytesAvailable = 0;
-#endif
if (d->hostLookupId != -1) {
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
@@ -1395,8 +1392,11 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint
}
#endif
- if (!d_func()->isBuffered)
- openMode |= QAbstractSocket::Unbuffered;
+ if (openMode & QIODevice::Unbuffered)
+ d->isBuffered = false; // Unbuffered QTcpSocket
+ else if (!d_func()->isBuffered)
+ openMode |= QAbstractSocket::Unbuffered; // QUdpSocket
+
QIODevice::open(openMode);
d->state = HostLookupState;
emit stateChanged(d->state);
@@ -1476,10 +1476,12 @@ qint64 QAbstractSocket::bytesAvailable() const
{
Q_D(const QAbstractSocket);
qint64 available = QIODevice::bytesAvailable();
- if (d->isBuffered)
- available += (qint64) d->readBuffer.size();
- else if (d->socketEngine && d->socketEngine->isValid())
+
+ available += (qint64) d->readBuffer.size();
+
+ if (!d->isBuffered && d->socketEngine && d->socketEngine->isValid())
available += d->socketEngine->bytesAvailable();
+
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::bytesAvailable() == %llu", available);
#endif
@@ -1612,6 +1614,10 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock
setErrorString(tr("Operation on socket is not supported"));
return false;
}
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket engine (if it has been set)
+ d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
+#endif
bool result = d->socketEngine->initialize(socketDescriptor, socketState);
if (!result) {
d->socketError = d->socketEngine->error();
@@ -1637,17 +1643,6 @@ bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState sock
d->peerAddress = d->socketEngine->peerAddress();
d->cachedSocketDescriptor = socketDescriptor;
-#ifdef Q_OS_LINUX
- // ### This is a workaround for certain broken Linux kernels, when using
- // QTcpSocket with a Unix domain socket. It was introduced around 2.6.9,
- // and fixed at some point after that.
- // http://archive.linux-usenet.com/index-t-73300.html
- // We can provide a better workaround for this: readFromSocket() can loop
- // while reading, but this must happen without triggering an implicit
- // close because of reading after the socket has closed.
- d->addToBytesAvailable = 4096;
-#endif
-
return true;
}
@@ -1677,6 +1672,14 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons
case KeepAliveOption:
d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveOption, value.toInt());
break;
+
+ case MulticastTtlOption:
+ d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastTtlOption, value.toInt());
+ break;
+
+ case MulticastLoopbackOption:
+ d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopbackOption, value.toInt());
+ break;
}
}
@@ -1706,6 +1709,13 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option)
case KeepAliveOption:
ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveOption);
break;
+
+ case MulticastTtlOption:
+ ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastTtlOption);
+ break;
+ case MulticastLoopbackOption:
+ ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastLoopbackOption);
+ break;
}
if (ret == -1)
return QVariant();
@@ -1781,6 +1791,14 @@ bool QAbstractSocket::waitForConnected(int msecs)
#endif
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+ QVariant v(property("_q_networksession"));
+ if (v.isValid()) {
+ networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v);
+ d->_q_startConnecting(QHostInfoPrivate::fromName(d->hostName, networkSession));
+ } else
+#endif
d->_q_startConnecting(QHostInfo::fromName(d->hostName));
}
if (state() == UnconnectedState)
@@ -1863,7 +1881,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
}
Q_ASSERT(d->socketEngine);
- forever {
+ do {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
@@ -1890,7 +1908,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
if (state() != ConnectedState)
return false;
- }
+ } while (msecs == -1 || qt_timeout_value(msecs, stopWatch.elapsed()) > 0);
return false;
}
@@ -2123,42 +2141,51 @@ bool QAbstractSocket::flush()
qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
{
Q_D(QAbstractSocket);
- if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid())
- d->socketEngine->setReadNotificationEnabled(true);
-
- if (!d->isBuffered) {
- if (!d->socketEngine)
- return -1; // no socket engine is probably EOF
- qint64 readBytes = d->socketEngine->read(data, maxSize);
- if (readBytes < 0) {
- d->socketError = d->socketEngine->error();
- setErrorString(d->socketEngine->errorString());
- }
- if (!d->socketEngine->isReadNotificationEnabled())
- d->socketEngine->setReadNotificationEnabled(true);
-#if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld",
- data, qt_prettyDebug(data, 32, readBytes).data(), maxSize,
- readBytes);
-#endif
- return readBytes;
- }
- if (d->readBuffer.isEmpty())
+ // This is for a buffered QTcpSocket
+ if (d->isBuffered && d->readBuffer.isEmpty())
// if we're still connected, return 0 indicating there may be more data in the future
// if we're not connected, return -1 indicating EOF
return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1);
- // If readFromSocket() read data, copy it to its destination.
- if (maxSize == 1) {
+ // short cut for a char read if we have something in the buffer
+ if (maxSize == 1 && !d->readBuffer.isEmpty()) {
*data = d->readBuffer.getChar();
#if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1",
+ qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1 [char buffer]",
data, isprint(int(uchar(*data))) ? *data : '?', *data);
#endif
+ if (d->readBuffer.isEmpty() && d->socketEngine && d->socketEngine->isValid())
+ d->socketEngine->setReadNotificationEnabled(true);
return 1;
}
+ // Special case for an Unbuffered QTcpSocket
+ // Re-filling the buffer.
+ if (d->socketType == TcpSocket
+ && !d->isBuffered
+ && d->readBuffer.size() < maxSize
+ && d->readBufferMaxSize > 0
+ && maxSize < d->readBufferMaxSize
+ && d->socketEngine
+ && d->socketEngine->isValid()) {
+ // Our buffer is empty and a read() was requested for a byte amount that is smaller
+ // than the readBufferMaxSize. This means that we should fill our buffer since we want
+ // such small reads come from the buffer and not always go to the costly socket engine read()
+ qint64 bytesToRead = d->socketEngine->bytesAvailable();
+ if (bytesToRead > 0) {
+ char *ptr = d->readBuffer.reserve(bytesToRead);
+ qint64 readBytes = d->socketEngine->read(ptr, bytesToRead);
+ if (readBytes == -2) {
+ // No bytes currently available for reading.
+ d->readBuffer.chop(bytesToRead);
+ } else {
+ d->readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes)));
+ }
+ }
+ }
+
+ // First try to satisfy the read from the buffer
qint64 bytesToRead = qMin(qint64(d->readBuffer.size()), maxSize);
qint64 readSoFar = 0;
while (readSoFar < bytesToRead) {
@@ -2170,8 +2197,56 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
d->readBuffer.free(bytesToReadFromThisBlock);
}
+ if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid())
+ d->socketEngine->setReadNotificationEnabled(true);
+
+ if (readSoFar > 0) {
+ // we read some data from buffer.
+ // Just return, readyRead will be emitted again
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', %lli) == %lli [buffer]",
+ data, isprint(int(uchar(*data))) ? *data : '?', *data, maxSize, readSoFar);
+#endif
+
+ if (d->readBuffer.isEmpty() && d->socketEngine)
+ d->socketEngine->setReadNotificationEnabled(true);
+ return readSoFar;
+ }
+
+ // This code path is for Unbuffered QTcpSocket or for connected UDP
+
+ if (!d->isBuffered) {
+ if (!d->socketEngine)
+ return -1; // no socket engine is probably EOF
+ if (!d->socketEngine->isValid())
+ return -1; // This is for unbuffered TCP when we already had been disconnected
+ if (d->state != QAbstractSocket::ConnectedState)
+ return -1; // This is for unbuffered TCP if we're not connected yet
+ qint64 readBytes = d->socketEngine->read(data, maxSize);
+ if (readBytes == -2) {
+ // -2 from the engine means no bytes available (EAGAIN) so read more later
+ return 0;
+ } else if (readBytes < 0) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+ d->resetSocketLayer();
+ d->state = QAbstractSocket::UnconnectedState;
+ } else if (!d->socketEngine->isReadNotificationEnabled()) {
+ // Only do this when there was no error
+ d->socketEngine->setReadNotificationEnabled(true);
+ }
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [engine]",
+ data, qt_prettyDebug(data, 32, readBytes).data(), maxSize,
+ readBytes);
+#endif
+ return readBytes;
+ }
+
+
#if defined (QABSTRACTSOCKET_DEBUG)
- qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld",
+ qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld [unreachable]",
data, qt_prettyDebug(data, qMin<qint64>(32, readSoFar), readSoFar).data(),
maxSize, readSoFar);
#endif
@@ -2196,7 +2271,23 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
return -1;
}
- if (!d->isBuffered) {
+ if (!d->isBuffered && d->socketType == TcpSocket && d->writeBuffer.isEmpty()) {
+ // This code is for the new Unbuffered QTcpSocket use case
+ qint64 written = d->socketEngine->write(data, size);
+ if (written < 0) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+ return written;
+ } else if (written < size) {
+ // Buffer what was not written yet
+ char *ptr = d->writeBuffer.reserve(size - written);
+ memcpy(ptr, data + written, size - written);
+ if (d->socketEngine)
+ d->socketEngine->setWriteNotificationEnabled(true);
+ }
+ return size; // size=actually written + what has been buffered
+ } else if (!d->isBuffered && d->socketType != TcpSocket) {
+ // This is for a QUdpSocket that was connect()ed
qint64 written = d->socketEngine->write(data, size);
if (written < 0) {
d->socketError = d->socketEngine->error();
@@ -2215,6 +2306,12 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
return written;
}
+ // This is the code path for normal buffered QTcpSocket or
+ // unbuffered QTcpSocket when there was already something in the
+ // write buffer and therefore we could not do a direct engine write.
+ // We just write to our write buffer and enable the write notifier
+ // The write notifier then flush()es the buffer.
+
char *ptr = d->writeBuffer.reserve(size);
if (size == 1)
*ptr = *data;
@@ -2553,7 +2650,7 @@ void QAbstractSocket::setReadBufferSize(qint64 size)
// ensure that the read notification is enabled if we've now got
// room in the read buffer
// but only if we're not inside canReadNotification -- that will take care on its own
- if (size == 0 || d->readBuffer.size() < size)
+ if ((size == 0 || d->readBuffer.size() < size) && d->state == QAbstractSocket::ConnectedState) // Do not change the notifier unless we are connected.
d->socketEngine->setReadNotificationEnabled(true);
}
}
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index 348824a..1959fab 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -64,6 +64,7 @@ class QAuthenticator;
class Q_NETWORK_EXPORT QAbstractSocket : public QIODevice
{
Q_OBJECT
+ Q_ENUMS(SocketType NetworkLayerProtocol SocketError SocketState SocketOption)
public:
enum SocketType {
TcpSocket,
@@ -118,7 +119,9 @@ public:
};
enum SocketOption {
LowDelayOption, // TCP_NODELAY
- KeepAliveOption // SO_KEEPALIVE
+ KeepAliveOption, // SO_KEEPALIVE
+ MulticastTtlOption, // IP_MULTICAST_TTL
+ MulticastLoopbackOption // IP_MULTICAST_LOOPBACK
};
QAbstractSocket(SocketType socketType, QObject *parent);
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index b600b30..cf7d988 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -59,7 +59,7 @@
#include "QtCore/qtimer.h"
#include "private/qringbuffer_p.h"
#include "private/qiodevice_p.h"
-#include "private/qnativesocketengine_p.h"
+#include "private/qabstractsocketengine_p.h"
#include "qnetworkproxy.h"
QT_BEGIN_NAMESPACE
@@ -138,9 +138,6 @@ public:
void setupSocketNotifiers();
bool readFromSocket();
-#ifdef Q_OS_LINUX
- qint64 addToBytesAvailable;
-#endif
qint64 readBufferMaxSize;
QRingBuffer readBuffer;
QRingBuffer writeBuffer;
diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp
index 41bf5df..2c5b5dc 100644
--- a/src/network/socket/qabstractsocketengine.cpp
+++ b/src/network/socket/qabstractsocketengine.cpp
@@ -40,7 +40,13 @@
****************************************************************************/
#include "qabstractsocketengine_p.h"
+
+#ifdef Q_OS_SYMBIAN
+#include "qsymbiansocketengine_p.h"
+#else
#include "qnativesocketengine_p.h"
+#endif
+
#include "qmutex.h"
#include "qnetworkproxy.h"
@@ -113,7 +119,11 @@ QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(QAbstractSocket
return 0;
#endif
+#ifdef Q_OS_SYMBIAN
+ return new QSymbianSocketEngine(parent);
+#else
return new QNativeSocketEngine(parent);
+#endif
}
QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(int socketDescripter, QObject *parent)
@@ -123,7 +133,11 @@ QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(int socketDescr
if (QAbstractSocketEngine *ret = socketHandlers()->at(i)->createSocketEngine(socketDescripter, parent))
return ret;
}
+#ifdef Q_OS_SYMBIAN
+ return new QSymbianSocketEngine(parent);
+#else
return new QNativeSocketEngine(parent);
+#endif
}
QAbstractSocket::SocketError QAbstractSocketEngine::error() const
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index 9ed007b..a8be98a 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -61,6 +61,9 @@ QT_BEGIN_NAMESPACE
class QAuthenticator;
class QAbstractSocketEnginePrivate;
+#ifndef QT_NO_NETWORKINTERFACE
+class QNetworkInterface;
+#endif
class QNetworkProxy;
class QAbstractSocketEngineReceiver {
@@ -94,7 +97,9 @@ public:
BindExclusively,
ReceiveOutOfBandData,
LowDelayOption,
- KeepAliveOption
+ KeepAliveOption,
+ MulticastTtlOption,
+ MulticastLoopbackOption
};
virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0;
@@ -118,13 +123,22 @@ public:
virtual qint64 write(const char *data, qint64 len) = 0;
#ifndef QT_NO_UDPSOCKET
+#ifndef QT_NO_NETWORKINTERFACE
+ virtual bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface) = 0;
+ virtual bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface) = 0;
+ virtual QNetworkInterface multicastInterface() const = 0;
+ virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0;
+#endif // QT_NO_NETWORKINTERFACE
+
virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0,
quint16 *port = 0) = 0;
virtual qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr,
quint16 port) = 0;
virtual bool hasPendingDatagrams() const = 0;
virtual qint64 pendingDatagramSize() const = 0;
-#endif
+#endif // QT_NO_UDPSOCKET
virtual qint64 bytesToWrite() const = 0;
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index b002bec..5c672ec 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -45,6 +45,7 @@
#include "qurl.h"
#include "qhttp.h"
#include "qelapsedtimer.h"
+#include "qnetworkinterface.h"
#if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP)
#include <qdebug.h>
@@ -71,6 +72,9 @@ bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSo
setProtocol(protocol);
setSocketType(type);
d->socket = new QTcpSocket(this);
+#ifndef QT_NO_BEARERMANAGEMENT
+ d->socket->setProperty("_q_networkSession", property("_q_networkSession"));
+#endif
// Explicitly disable proxying on the proxy socket itself to avoid
// unwanted recursion.
@@ -241,6 +245,36 @@ qint64 QHttpSocketEngine::write(const char *data, qint64 len)
}
#ifndef QT_NO_UDPSOCKET
+#ifndef QT_NO_NETWORKINTERFACE
+bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &,
+ const QNetworkInterface &)
+{
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Operation on socket is not supported"));
+ return false;
+}
+
+bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &,
+ const QNetworkInterface &)
+{
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Operation on socket is not supported"));
+ return false;
+}
+
+QNetworkInterface QHttpSocketEngine::multicastInterface() const
+{
+ return QNetworkInterface();
+}
+
+bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &)
+{
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Operation on socket is not supported"));
+ return false;
+}
+#endif // QT_NO_NETWORKINTERFACE
+
qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *,
quint16 *)
{
@@ -683,11 +717,10 @@ void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
d->state = None;
setError(error, d->socket->errorString());
- if (error == QAbstractSocket::RemoteHostClosedError) {
- emitReadNotification();
- } else {
+ if (error != QAbstractSocket::RemoteHostClosedError)
qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
- }
+ //read notification needs to always be emitted, otherwise the higher layer doesn't get the disconnected signal
+ emitReadNotification();
}
void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
@@ -722,7 +755,10 @@ void QHttpSocketEngine::emitReadNotification()
{
Q_D(QHttpSocketEngine);
d->readNotificationActivated = true;
- if (d->readNotificationEnabled && !d->readNotificationPending) {
+ // if there is a connection notification pending we have to emit the readNotification
+ // incase there is connection error. This is only needed for Windows, but it does not
+ // hurt in other cases.
+ if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) {
d->readNotificationPending = true;
QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection);
}
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
index 00744f3..d7cc7c1 100644
--- a/src/network/socket/qhttpsocketengine_p.h
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -102,6 +102,15 @@ public:
qint64 write(const char *data, qint64 len);
#ifndef QT_NO_UDPSOCKET
+#ifndef QT_NO_NETWORKINTERFACE
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &interface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &interface);
+ QNetworkInterface multicastInterface() const;
+ bool setMulticastInterface(const QNetworkInterface &iface);
+#endif // QT_NO_NETWORKINTERFACE
+
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0,
quint16 *port = 0);
qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr,
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index f322e23..d4f847a 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -274,11 +274,11 @@ QLocalSocket *QLocalServer::nextPendingConnection()
if (d->pendingConnections.isEmpty())
return 0;
QLocalSocket *nextSocket = d->pendingConnections.dequeue();
+#ifndef QT_LOCALSOCKET_TCP
#ifdef Q_OS_SYMBIAN
if(!d->socketNotifier)
return nextSocket;
#endif
-#ifndef QT_LOCALSOCKET_TCP
if (d->pendingConnections.size() <= d->maxPendingConnections)
#ifndef Q_OS_WIN
d->socketNotifier->setEnabled(true);
diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h
index 699fb2c..67b7000 100644
--- a/src/network/socket/qlocalserver_p.h
+++ b/src/network/socket/qlocalserver_p.h
@@ -65,7 +65,7 @@
# include <qt_windows.h>
# include <private/qwineventnotifier_p.h>
#else
-# include <private/qnativesocketengine_p.h>
+# include <private/qabstractsocketengine_p.h>
# include <qsocketnotifier.h>
#endif
diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp
index 029af79..219d2aa 100644
--- a/src/network/socket/qlocalsocket.cpp
+++ b/src/network/socket/qlocalsocket.cpp
@@ -346,7 +346,7 @@ QLocalSocket::QLocalSocket(QObject * parent)
QLocalSocket::~QLocalSocket()
{
close();
-#ifndef Q_OS_WIN
+#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP)
Q_D(QLocalSocket);
d->unixSocket.setParent(0);
#endif
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index aaef998..2671258 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -67,7 +67,7 @@
# include "private/qringbuffer_p.h"
# include <private/qwineventnotifier_p.h>
#else
-# include "private/qnativesocketengine_p.h"
+# include "private/qabstractsocketengine_p.h"
# include <qtcpsocket.h>
# include <qsocketnotifier.h>
# include <errno.h>
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
index b5dcb05..f595ba7 100644
--- a/src/network/socket/qlocalsocket_win.cpp
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -79,6 +79,11 @@ void QLocalSocketPrivate::setErrorString(const QString &function)
errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
state = QLocalSocket::UnconnectedState;
break;
+ case ERROR_ACCESS_DENIED:
+ error = QLocalSocket::SocketAccessError;
+ errorString = QLocalSocket::tr("%1: Access denied").arg(function);
+ state = QLocalSocket::UnconnectedState;
+ break;
default:
error = QLocalSocket::UnknownSocketError;
errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError);
@@ -348,6 +353,11 @@ qint64 QLocalSocket::writeData(const char *data, qint64 maxSize)
void QLocalSocket::abort()
{
+ Q_D(QLocalSocket);
+ if (d->pipeWriter) {
+ delete d->pipeWriter;
+ d->pipeWriter = 0;
+ }
close();
}
@@ -569,10 +579,7 @@ bool QLocalSocket::waitForDisconnected(int msecs)
bool QLocalSocket::isValid() const
{
Q_D(const QLocalSocket);
- if (d->handle == INVALID_HANDLE_VALUE)
- return false;
-
- return PeekNamedPipe(d->handle, NULL, 0, NULL, NULL, NULL);
+ return d->handle != INVALID_HANDLE_VALUE;
}
bool QLocalSocket::waitForReadyRead(int msecs)
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index eb2024d..8758522 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -98,6 +98,7 @@
#include <qabstracteventdispatcher.h>
#include <qsocketnotifier.h>
+#include <qnetworkinterface.h>
#include "qnativesocketengine_p.h"
#include <private/qthread_p.h>
@@ -157,12 +158,12 @@ QT_BEGIN_NAMESPACE
concurrent QNativeSocketEngine. This is safe, because WSAStartup and
WSACleanup are reference counted.
*/
-QNativeSocketEnginePrivate::QNativeSocketEnginePrivate()
+QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() :
+ socketDescriptor(-1),
+ readNotifier(0),
+ writeNotifier(0),
+ exceptNotifier(0)
{
- socketDescriptor = -1;
- readNotifier = 0;
- writeNotifier = 0;
- exceptNotifier = 0;
}
/*! \internal
@@ -386,7 +387,6 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb
// Make sure we receive out-of-band data
- // On Symbian OS this works only with native IP stack, not with WinSock
if (socketType == QAbstractSocket::TcpSocket
&& !setOption(ReceiveOutOfBandData, 1)) {
qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");
@@ -646,6 +646,54 @@ int QNativeSocketEngine::accept()
return d->nativeAccept();
}
+#ifndef QT_NO_NETWORKINTERFACE
+
+/*!
+ \since 4.8
+*/
+bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false);
+ Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false);
+ return d->nativeJoinMulticastGroup(groupAddress, iface);
+}
+
+/*!
+ \since 4.8
+*/
+bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false);
+ Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false);
+ return d->nativeLeaveMulticastGroup(groupAddress, iface);
+}
+
+/*! \since 4.8 */
+QNetworkInterface QNativeSocketEngine::multicastInterface() const
+{
+ Q_D(const QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface());
+ Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface());
+ return d->nativeMulticastInterface();
+}
+
+/*! \since 4.8 */
+bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false);
+ Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false);
+ return d->nativeSetMulticastInterface(iface);
+}
+
+#endif // QT_NO_NETWORKINTERFACE
+
/*!
Returns the number of bytes that are currently available for
reading. On error, -1 is returned.
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index fa52816..c5c4b32 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -60,11 +60,6 @@
# include <winsock2.h>
#endif
-#ifdef Q_OS_SYMBIAN
-#include <private/qeventdispatcher_symbian_p.h>
-#include <unistd.h>
-#endif
-
QT_BEGIN_NAMESPACE
// Use our own defines and structs which we know are correct
@@ -101,6 +96,9 @@ union qt_sockaddr {
};
class QNativeSocketEnginePrivate;
+#ifndef QT_NO_NETWORKINTERFACE
+class QNetworkInterface;
+#endif
class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine
{
@@ -123,6 +121,15 @@ public:
int accept();
void close();
+#ifndef QT_NO_NETWORKINTERFACE
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ QNetworkInterface multicastInterface() const;
+ bool setMulticastInterface(const QNetworkInterface &iface);
+#endif
+
qint64 bytesAvailable() const;
qint64 read(char *data, qint64 maxlen);
@@ -237,6 +244,14 @@ public:
bool nativeBind(const QHostAddress &address, quint16 port);
bool nativeListen(int backlog);
int nativeAccept();
+#ifndef QT_NO_NETWORKINTERFACE
+ bool nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ QNetworkInterface nativeMulticastInterface() const;
+ bool nativeSetMulticastInterface(const QNetworkInterface &iface);
+#endif
qint64 nativeBytesAvailable() const;
bool nativeHasPendingDatagrams() const;
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index 5cf27ce..39570c8 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -46,6 +46,7 @@
#include "qhostaddress.h"
#include "qelapsedtimer.h"
#include "qvarlengtharray.h"
+#include "qnetworkinterface.h"
#include <time.h>
#include <errno.h>
#include <fcntl.h>
@@ -64,12 +65,7 @@
#include <ctype.h>
#endif
-#ifdef Q_OS_SYMBIAN // ### TODO: Are these headers right?
-#include <sys/socket.h>
-#include <netinet/in.h>
-#else
#include <netinet/tcp.h>
-#endif
QT_BEGIN_NAMESPACE
@@ -173,11 +169,8 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
int protocol = AF_INET;
#endif
int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
-#ifdef Q_OS_SYMBIAN
- int socket = ::socket(protocol, type, 0);
-#else
+
int socket = qt_safe_socket(protocol, type, 0);
-#endif
if (socket <= 0) {
switch (errno) {
@@ -244,6 +237,30 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
case QNativeSocketEngine::KeepAliveOption:
n = SO_KEEPALIVE;
break;
+ case QNativeSocketEngine::MulticastTtlOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_HOPS;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_TTL;
+ }
+ break;
+ case QNativeSocketEngine::MulticastLoopbackOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_LOOP;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_LOOP;
+ }
+ break;
}
int v = -1;
@@ -295,11 +312,9 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
}
#else // Q_OS_VXWORKS
int onoff = 1;
-#ifdef Q_OS_SYMBIAN
- if (::ioctl(socketDescriptor, FIONBIO, &onoff) < 0) {
-#else
+
if (qt_safe_ioctl(socketDescriptor, FIONBIO, &onoff) < 0) {
-#endif
+
#ifdef QNATIVESOCKETENGINE_DEBUG
perror("QNativeSocketEnginePrivate::setOption(): ioctl(FIONBIO, 1) failed");
#endif
@@ -309,7 +324,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
return true;
}
case QNativeSocketEngine::AddressReusable:
-#if defined(SO_REUSEPORT) && !defined(Q_OS_SYMBIAN)
+#if defined(SO_REUSEPORT)
n = SO_REUSEPORT;
#else
n = SO_REUSEADDR;
@@ -327,6 +342,30 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
case QNativeSocketEngine::KeepAliveOption:
n = SO_KEEPALIVE;
break;
+ case QNativeSocketEngine::MulticastTtlOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_HOPS;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_TTL;
+ }
+ break;
+ case QNativeSocketEngine::MulticastLoopbackOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_LOOP;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_LOOP;
+ }
+ break;
}
return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0;
@@ -378,11 +417,8 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16
} else {
// unreachable
}
-#ifdef Q_OS_SYMBIAN
- int connectResult = ::connect(socketDescriptor, sockAddrPtr, sockAddrSize);
-#else
+
int connectResult = qt_safe_connect(socketDescriptor, sockAddrPtr, sockAddrSize);
-#endif
if (connectResult == -1) {
switch (errno) {
case EISCONN:
@@ -425,9 +461,6 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16
case EBADF:
case EFAULT:
case ENOTSOCK:
-#ifdef Q_OS_SYMBIAN
- case EPIPE:
-#endif
socketState = QAbstractSocket::UnconnectedState;
default:
break;
@@ -526,11 +559,7 @@ bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16
bool QNativeSocketEnginePrivate::nativeListen(int backlog)
{
-#ifdef Q_OS_SYMBIAN
- if (::listen(socketDescriptor, backlog) < 0) {
-#else
if (qt_safe_listen(socketDescriptor, backlog) < 0) {
-#endif
switch (errno) {
case EADDRINUSE:
setError(QAbstractSocket::AddressInUseError,
@@ -557,25 +586,185 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog)
int QNativeSocketEnginePrivate::nativeAccept()
{
-#ifdef Q_OS_SYMBIAN
- int acceptedDescriptor = ::accept(socketDescriptor, 0, 0);
-#else
int acceptedDescriptor = qt_safe_accept(socketDescriptor, 0, 0);
-#endif
return acceptedDescriptor;
}
+#ifndef QT_NO_NETWORKINTERFACE
+
+static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d,
+ int how6,
+ int how4,
+ const QHostAddress &groupAddress,
+ const QNetworkInterface &interface)
+{
+ int level = 0;
+ int sockOpt = 0;
+ void *sockArg;
+ int sockArgSize;
+
+ ip_mreq mreq4;
+#ifndef QT_NO_IPV6
+ ipv6_mreq mreq6;
+
+ if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ sockOpt = how6;
+ sockArg = &mreq6;
+ sockArgSize = sizeof(mreq6);
+ memset(&mreq6, 0, sizeof(mreq6));
+ Q_IPV6ADDR ip6 = groupAddress.toIPv6Address();
+ memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6));
+ mreq6.ipv6mr_interface = interface.index();
+ } else
+#endif
+ if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ level = IPPROTO_IP;
+ sockOpt = how4;
+ sockArg = &mreq4;
+ sockArgSize = sizeof(mreq4);
+ memset(&mreq4, 0, sizeof(mreq4));
+ mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address());
+
+ if (interface.isValid()) {
+ QList<QNetworkAddressEntry> addressEntries = interface.addressEntries();
+ if (!addressEntries.isEmpty()) {
+ QHostAddress firstIP = addressEntries.first().ip();
+ mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address());
+ } else {
+ d->setError(QAbstractSocket::NetworkError,
+ QNativeSocketEnginePrivate::NetworkUnreachableErrorString);
+ return false;
+ }
+ } else {
+ mreq4.imr_interface.s_addr = INADDR_ANY;
+ }
+ } else {
+ // unreachable
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString);
+ return false;
+ }
+
+ int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize);
+ if (res == -1) {
+ switch (errno) {
+ case ENOPROTOOPT:
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::OperationUnsupportedErrorString);
+ break;
+ case EADDRNOTAVAIL:
+ d->setError(QAbstractSocket::SocketAddressNotAvailableError,
+ QNativeSocketEnginePrivate::AddressNotAvailableErrorString);
+ break;
+ default:
+ d->setError(QAbstractSocket::UnknownSocketError,
+ QNativeSocketEnginePrivate::UnknownSocketErrorString);
+ break;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &interface)
+{
+ return multicastMembershipHelper(this,
+#ifndef QT_NO_IPV6
+ IPV6_JOIN_GROUP,
+#else
+ 0,
+#endif
+ IP_ADD_MEMBERSHIP,
+ groupAddress,
+ interface);
+}
+
+bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &interface)
+{
+ return multicastMembershipHelper(this,
+#ifndef QT_NO_IPV6
+ IPV6_LEAVE_GROUP,
+#else
+ 0,
+#endif
+ IP_DROP_MEMBERSHIP,
+ groupAddress,
+ interface);
+}
+
+QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const
+{
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ uint v;
+ QT_SOCKOPTLEN_T sizeofv = sizeof(v);
+ if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, &sizeofv) == -1)
+ return QNetworkInterface();
+ return QNetworkInterface::interfaceFromIndex(v);
+ }
+#endif
+
+ struct in_addr v = { 0 };
+ QT_SOCKOPTLEN_T sizeofv = sizeof(v);
+ if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, &sizeofv) == -1)
+ return QNetworkInterface();
+ if (v.s_addr != 0 && sizeofv >= sizeof(v)) {
+ QHostAddress ipv4(ntohl(v.s_addr));
+ QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
+ for (int i = 0; i < ifaces.count(); ++i) {
+ const QNetworkInterface &iface = ifaces.at(i);
+ QList<QNetworkAddressEntry> entries = iface.addressEntries();
+ for (int j = 0; j < entries.count(); ++j) {
+ const QNetworkAddressEntry &entry = entries.at(j);
+ if (entry.ip() == ipv4)
+ return iface;
+ }
+ }
+ }
+ return QNetworkInterface();
+}
+
+bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface)
+{
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ uint v = iface.index();
+ return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, sizeof(v)) != -1);
+ }
+#endif
+
+ struct in_addr v;
+ if (iface.isValid()) {
+ QList<QNetworkAddressEntry> entries = iface.addressEntries();
+ for (int i = 0; i < entries.count(); ++i) {
+ const QNetworkAddressEntry &entry = entries.at(i);
+ const QHostAddress &ip = entry.ip();
+ if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
+ v.s_addr = htonl(ip.toIPv4Address());
+ int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v));
+ if (r != -1)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ v.s_addr = INADDR_ANY;
+ return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)) != -1);
+}
+
+#endif // QT_NO_NETWORKINTERFACE
+
qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const
{
int nbytes = 0;
// gives shorter than true amounts on Unix domain sockets.
qint64 available = 0;
-#ifdef Q_OS_SYMBIAN
- if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0)
-#else
if (qt_safe_ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0)
-#endif
available = (qint64) nbytes;
#if defined (QNATIVESOCKETENGINE_DEBUG)
@@ -594,15 +783,10 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
// Peek 0 bytes into the next message. The size of the message may
// well be 0, so we can't check recvfrom's return value.
ssize_t readBytes;
-#ifdef Q_OS_SYMBIAN
- char c;
- readBytes = ::recvfrom(socketDescriptor, &c, 1, MSG_PEEK, &storage.a, &storageSize);
-#else
do {
char c;
readBytes = ::recvfrom(socketDescriptor, &c, 1, MSG_PEEK, &storage.a, &storageSize);
} while (readBytes == -1 && errno == EINTR);
-#endif
// If there's no error, or if our buffer was too small, there must be a
// pending datagram.
@@ -615,14 +799,6 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
return result;
}
-#ifdef Q_OS_SYMBIAN
-qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
-{
- size_t nbytes = 0;
- ::ioctl(socketDescriptor, E32IONREAD, (char *) &nbytes);
- return qint64(nbytes-28);
-}
-#else
qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
{
QVarLengthArray<char, 8192> udpMessagePeekBuffer(8192);
@@ -649,7 +825,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
return qint64(recvResult);
}
-#endif
+
qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize,
QHostAddress *address, quint16 *port)
{
@@ -659,17 +835,11 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
sz = sizeof(aa);
ssize_t recvFromResult = 0;
-#ifdef Q_OS_SYMBIAN
- char c;
- recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1,
- 0, &aa.a, &sz);
-#else
do {
char c;
recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1,
0, &aa.a, &sz);
} while (recvFromResult == -1 && errno == EINTR);
-#endif
if (recvFromResult == -1) {
setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
@@ -718,13 +888,8 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
// ignore the SIGPIPE signal
qt_ignore_sigpipe();
-#ifdef Q_OS_SYMBIAN
- ssize_t sentBytes = ::sendto(socketDescriptor, data, len,
- 0, sockAddrPtr, sockAddrSize);
-#else
ssize_t sentBytes = qt_safe_sendto(socketDescriptor, data, len,
0, sockAddrPtr, sockAddrSize);
-#endif
if (sentBytes < 0) {
switch (errno) {
@@ -822,11 +987,7 @@ void QNativeSocketEnginePrivate::nativeClose()
qDebug("QNativeSocketEngine::nativeClose()");
#endif
-#ifdef Q_OS_SYMBIAN
- ::close(socketDescriptor);
-#else
- qt_safe_close(socketDescriptor);
-#endif
+ qt_safe_close(socketDescriptor);
}
qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
@@ -837,12 +998,7 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
qt_ignore_sigpipe();
ssize_t writtenBytes;
-#ifdef Q_OS_SYMBIAN
- // Symbian does not support signals natively and Open C returns EINTR when moving to offline
- writtenBytes = ::write(socketDescriptor, data, len);
-#else
writtenBytes = qt_safe_write(socketDescriptor, data, len);
-#endif
if (writtenBytes < 0) {
switch (errno) {
@@ -877,16 +1033,12 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
{
Q_Q(QNativeSocketEngine);
if (!q->isValid()) {
- qWarning("QNativeSocketEngine::unbufferedRead: Invalid socket");
+ qWarning("QNativeSocketEngine::nativeRead: Invalid socket");
return -1;
}
ssize_t r = 0;
-#ifdef Q_OS_SYMBIAN
- r = ::read(socketDescriptor, data, maxSize);
-#else
r = qt_safe_read(socketDescriptor, data, maxSize);
-#endif
if (r < 0) {
r = -1;
@@ -903,9 +1055,6 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
case EIO:
//error string is now set in read(), not here in nativeRead()
break;
-#ifdef Q_OS_SYMBIAN
- case EPIPE:
-#endif
case ECONNRESET:
#if defined(Q_OS_VXWORKS)
case ESHUTDOWN:
@@ -936,40 +1085,11 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
-#ifdef Q_OS_SYMBIAN
- fd_set fdexception;
- FD_ZERO(&fdexception);
- FD_SET(socketDescriptor, &fdexception);
-#endif
-
int retval;
if (selectForRead)
-#ifdef Q_OS_SYMBIAN
- retval = ::select(socketDescriptor + 1, &fds, 0, &fdexception, timeout < 0 ? 0 : &tv);
-#else
retval = qt_safe_select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv);
-#endif
else
-#ifdef Q_OS_SYMBIAN
- retval = ::select(socketDescriptor + 1, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv);
-#else
retval = qt_safe_select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv);
-#endif
-
-
-#ifdef Q_OS_SYMBIAN
- bool selectForExec = false;
- if(retval != 0) {
- if(retval < 0) {
- qWarning("nativeSelect(....) returned < 0 for socket %d", socketDescriptor);
- }
- selectForExec = FD_ISSET(socketDescriptor, &fdexception);
- }
- if(selectForExec) {
- qWarning("nativeSelect (selectForRead %d, retVal %d, errno %d) Unexpected exception for fd %d",
- selectForRead, retval, errno, socketDescriptor);
- }
-#endif
return retval;
}
@@ -987,65 +1107,12 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
if (checkWrite)
FD_SET(socketDescriptor, &fdwrite);
-#ifdef Q_OS_SYMBIAN
- fd_set fdexception;
- FD_ZERO(&fdexception);
- FD_SET(socketDescriptor, &fdexception);
-#endif
-
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
int ret;
-#ifndef Q_OS_SYMBIAN
ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv);
-#else
- QElapsedTimer timer;
- timer.start();
-
- do {
- ret = ::select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
- bool selectForExec = false;
- if(ret != 0) {
- if(ret < 0) {
- qWarning("nativeSelect(....) returned < 0 for socket %d", socketDescriptor);
- }
- selectForExec = FD_ISSET(socketDescriptor, &fdexception);
- }
- if(selectForExec) {
- qWarning("nativeSelect (checkRead %d, checkWrite %d, ret %d, errno %d): Unexpected expectfds ready in fd %d",
- checkRead, checkWrite, ret, errno, socketDescriptor);
- if (checkWrite){
- FD_CLR(socketDescriptor, &fdread);
- FD_SET(socketDescriptor, &fdwrite);
- } else if (checkRead)
- FD_SET(socketDescriptor, &fdread);
-
-
- if ((ret == -1) && ( errno == ECONNREFUSED || errno == EPIPE ))
- ret = 1;
-
- }
-
- if (ret != -1 || errno != EINTR) {
- break;
- }
-
- if (timeout > 0) {
- // recalculate the timeout
- int t = timeout - timer.elapsed();
- if (t < 0) {
- // oops, timeout turned negative?
- ret = -1;
- break;
- }
-
- tv.tv_sec = t / 1000;
- tv.tv_usec = (t % 1000) * 1000;
- }
- } while (true);
-#endif
if (ret <= 0)
return ret;
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 6350651..88b87b9 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include <winsock2.h>
+#include <ws2tcpip.h>
#include "qnativesocketengine_p.h"
@@ -47,6 +48,7 @@
#include <qsocketnotifier.h>
#include <qdebug.h>
#include <qdatetime.h>
+#include <qnetworkinterface.h>
//#define QNATIVESOCKETENGINE_DEBUG
#if defined(QNATIVESOCKETENGINE_DEBUG)
@@ -335,15 +337,17 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
}
#if !defined(Q_OS_WINCE)
- // enable new behavior using
- // SIO_UDP_CONNRESET
- DWORD dwBytesReturned = 0;
- int bNewBehavior = 1;
- if (::WSAIoctl(socket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior),
- NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) {
- // not to worry isBogusUdpReadNotification() should handle this otherwise
- int err = WSAGetLastError();
- WS_ERROR_DEBUG(err);
+ if (socketType == QAbstractSocket::UdpSocket) {
+ // enable new behavior using
+ // SIO_UDP_CONNRESET
+ DWORD dwBytesReturned = 0;
+ int bNewBehavior = 1;
+ if (::WSAIoctl(socket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior),
+ NULL, 0, &dwBytesReturned, NULL, NULL) == SOCKET_ERROR) {
+ // not to worry isBogusUdpReadNotification() should handle this otherwise
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ }
}
#endif
@@ -399,6 +403,30 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
case QNativeSocketEngine::KeepAliveOption:
n = SO_KEEPALIVE;
break;
+ case QNativeSocketEngine::MulticastTtlOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_HOPS;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_TTL;
+ }
+ break;
+ case QNativeSocketEngine::MulticastLoopbackOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_LOOP;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_LOOP;
+ }
+ break;
}
int v = -1;
@@ -459,6 +487,30 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
case QNativeSocketEngine::KeepAliveOption:
n = SO_KEEPALIVE;
break;
+ case QNativeSocketEngine::MulticastTtlOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_HOPS;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_TTL;
+ }
+ break;
+ case QNativeSocketEngine::MulticastLoopbackOption:
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ n = IPV6_MULTICAST_LOOP;
+ } else
+#endif
+ {
+ level = IPPROTO_IP;
+ n = IP_MULTICAST_LOOP;
+ }
+ break;
}
if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) {
@@ -589,6 +641,11 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
socketState = QAbstractSocket::UnconnectedState;
break;
}
+ if (value == WSAEADDRNOTAVAIL) {
+ setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
}
// fall through
}
@@ -648,8 +705,26 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
}
-bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 port)
+bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port)
{
+ QHostAddress address = a;
+ switch (address.protocol()) {
+ case QAbstractSocket::IPv6Protocol:
+ if (address.toIPv6Address()[0] == 0xff) {
+ // binding to a multicast address
+ address = QHostAddress(QHostAddress::AnyIPv6);
+ }
+ break;
+ case QAbstractSocket::IPv4Protocol:
+ if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) {
+ // binding to a multicast address
+ address = QHostAddress(QHostAddress::Any);
+ }
+ break;
+ default:
+ break;
+ }
+
struct sockaddr_in sockAddrIPv4;
qt_sockaddr_in6 sockAddrIPv6;
struct sockaddr *sockAddrPtr = 0;
@@ -747,6 +822,160 @@ int QNativeSocketEnginePrivate::nativeAccept()
return acceptedDescriptor;
}
+static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d,
+ int how6,
+ int how4,
+ const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ int level = 0;
+ int sockOpt = 0;
+ char *sockArg;
+ int sockArgSize;
+
+ struct ip_mreq mreq4;
+#ifndef QT_NO_IPV6
+ struct ipv6_mreq mreq6;
+
+ if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ level = IPPROTO_IPV6;
+ sockOpt = how6;
+ sockArg = reinterpret_cast<char *>(&mreq6);
+ sockArgSize = sizeof(mreq6);
+ memset(&mreq6, 0, sizeof(mreq6));
+ Q_IPV6ADDR ip6 = groupAddress.toIPv6Address();
+ memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6));
+ mreq6.ipv6mr_interface = iface.index();
+ } else
+#endif
+ if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) {
+ level = IPPROTO_IP;
+ sockOpt = how4;
+ sockArg = reinterpret_cast<char *>(&mreq4);
+ sockArgSize = sizeof(mreq4);
+ memset(&mreq4, 0, sizeof(mreq4));
+ mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address());
+
+ if (iface.isValid()) {
+ QList<QNetworkAddressEntry> addressEntries = iface.addressEntries();
+ if (!addressEntries.isEmpty()) {
+ QHostAddress firstIP = addressEntries.first().ip();
+ mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address());
+ } else {
+ d->setError(QAbstractSocket::NetworkError,
+ QNativeSocketEnginePrivate::NetworkUnreachableErrorString);
+ return false;
+ }
+ } else {
+ mreq4.imr_interface.s_addr = INADDR_ANY;
+ }
+ } else {
+ // unreachable
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString);
+ return false;
+ }
+
+ int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize);
+ if (res == -1) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::OperationUnsupportedErrorString);
+ return false;
+ }
+ return true;
+}
+
+bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ return multicastMembershipHelper(this,
+#ifndef QT_NO_IPV6
+ IPV6_JOIN_GROUP,
+#else
+ 0,
+#endif
+ IP_ADD_MEMBERSHIP,
+ groupAddress,
+ iface);
+}
+
+bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ return multicastMembershipHelper(this,
+#ifndef QT_NO_IPV6
+ IPV6_LEAVE_GROUP,
+#else
+ 0,
+#endif
+ IP_DROP_MEMBERSHIP,
+ groupAddress,
+ iface);
+}
+
+QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const
+{
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ uint v;
+ QT_SOCKOPTLEN_T sizeofv = sizeof(v);
+ if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, &sizeofv) == -1)
+ return QNetworkInterface();
+ return QNetworkInterface::interfaceFromIndex(v);
+ }
+#endif
+
+ struct in_addr v;
+ v.s_addr = 0;
+ QT_SOCKOPTLEN_T sizeofv = sizeof(v);
+ if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, &sizeofv) == -1)
+ return QNetworkInterface();
+ if (v.s_addr != 0 && sizeofv >= QT_SOCKOPTLEN_T(sizeof(v))) {
+ QHostAddress ipv4(ntohl(v.s_addr));
+ QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
+ for (int i = 0; i < ifaces.count(); ++i) {
+ const QNetworkInterface &iface = ifaces.at(i);
+ if (!(iface.flags() & QNetworkInterface::CanMulticast))
+ continue;
+ QList<QNetworkAddressEntry> entries = iface.addressEntries();
+ for (int j = 0; j < entries.count(); ++j) {
+ const QNetworkAddressEntry &entry = entries.at(j);
+ if (entry.ip() == ipv4)
+ return iface;
+ }
+ }
+ }
+ return QNetworkInterface();
+}
+
+bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface)
+{
+#ifndef QT_NO_IPV6
+ if (socketProtocol == QAbstractSocket::IPv6Protocol) {
+ uint v = iface.isValid() ? iface.index() : 0;
+ return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, sizeof(v)) != -1);
+ }
+#endif
+
+ struct in_addr v;
+ if (iface.isValid()) {
+ QList<QNetworkAddressEntry> entries = iface.addressEntries();
+ for (int i = 0; i < entries.count(); ++i) {
+ const QNetworkAddressEntry &entry = entries.at(i);
+ const QHostAddress &ip = entry.ip();
+ if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
+ v.s_addr = htonl(ip.toIPv4Address());
+ int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v));
+ if (r != -1)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ v.s_addr = INADDR_ANY;
+ return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)) != -1);
+}
qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const
{
@@ -816,7 +1045,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
bool result = false;
fd_set readS;
FD_ZERO(&readS);
- FD_SET(socketDescriptor, &readS);
+ FD_SET((SOCKET)socketDescriptor, &readS);
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 5000;
@@ -1111,7 +1340,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
memset(&fds, 0, sizeof(fd_set));
fds.fd_count = 1;
- fds.fd_array[0] = socketDescriptor;
+ fds.fd_array[0] = (SOCKET)socketDescriptor;
struct timeval tv;
tv.tv_sec = timeout / 1000;
@@ -1125,12 +1354,12 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
// Windows needs this to report errors when connecting a socket ...
fd_set fdexception;
FD_ZERO(&fdexception);
- FD_SET(socketDescriptor, &fdexception);
+ FD_SET((SOCKET)socketDescriptor, &fdexception);
ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv);
// ... but if it is actually set, pretend it did not happen
- if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception))
+ if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
ret--;
}
@@ -1157,16 +1386,16 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
memset(&fdread, 0, sizeof(fd_set));
if (checkRead) {
fdread.fd_count = 1;
- fdread.fd_array[0] = socketDescriptor;
+ fdread.fd_array[0] = (SOCKET)socketDescriptor;
}
memset(&fdwrite, 0, sizeof(fd_set));
FD_ZERO(&fdexception);
if (checkWrite) {
fdwrite.fd_count = 1;
- fdwrite.fd_array[0] = socketDescriptor;
+ fdwrite.fd_array[0] = (SOCKET)socketDescriptor;
// Windows needs this to report errors when connecting a socket
- FD_SET(socketDescriptor, &fdexception);
+ FD_SET((SOCKET)socketDescriptor, &fdexception);
}
struct timeval tv;
@@ -1180,7 +1409,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
#endif
//... but if it is actually set, pretend it did not happen
- if (ret > 0 && FD_ISSET(socketDescriptor, &fdexception))
+ if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
ret--;
if (readEnabled)
@@ -1189,8 +1418,8 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
if (ret <= 0)
return ret;
- *selectForRead = FD_ISSET(socketDescriptor, &fdread);
- *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite);
+ *selectForRead = FD_ISSET((SOCKET)socketDescriptor, &fdread);
+ *selectForWrite = FD_ISSET((SOCKET)socketDescriptor, &fdwrite);
return ret;
}
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index f7acc4e..ab75798 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -56,6 +56,7 @@
#include "qurl.h"
#include "qauthenticator.h"
#include <qendian.h>
+#include <qnetworkinterface.h>
QT_BEGIN_NAMESPACE
@@ -555,6 +556,9 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
udpData = new QSocks5UdpAssociateData;
data = udpData;
udpData->udpSocket = new QUdpSocket(q);
+#ifndef QT_NO_BEARERMANAGEMENT
+ udpData->udpSocket->setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
udpData->udpSocket->setProxy(QNetworkProxy::NoProxy);
QObject::connect(udpData->udpSocket, SIGNAL(readyRead()),
q, SLOT(_q_udpSocketReadNotification()),
@@ -566,6 +570,9 @@ void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
}
data->controlSocket = new QTcpSocket(q);
+#ifndef QT_NO_BEARERMANAGEMENT
+ data->controlSocket->setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
data->controlSocket->setProxy(QNetworkProxy::NoProxy);
QObject::connect(data->controlSocket, SIGNAL(connected()), q, SLOT(_q_controlSocketConnected()),
Qt::DirectConnection);
@@ -1377,6 +1384,9 @@ bool QSocks5SocketEngine::bind(const QHostAddress &address, quint16 port)
d->udpData->associatePort = d->localPort;
d->localPort = 0;
QUdpSocket dummy;
+#ifndef QT_NO_BEARERMANAGEMENT
+ dummy.setProperty("_q_networksession", property("_q_networksession"));
+#endif
dummy.setProxy(QNetworkProxy::NoProxy);
if (!dummy.bind()
|| writeDatagram(0,0, d->data->controlSocket->localAddress(), dummy.localPort()) != 0
@@ -1532,8 +1542,13 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
// ### Handle this error.
}
- d->data->controlSocket->write(sealedBuf);
+ qint64 written = d->data->controlSocket->write(sealedBuf);
+ if (written <= 0) {
+ QSOCKS5_Q_DEBUG << "native write returned" << written;
+ return written;
+ }
d->data->controlSocket->waitForBytesWritten(0);
+ //NB: returning len rather than written for the OK case, because the "sealing" may increase the length
return len;
#ifndef QT_NO_UDPSOCKET
} else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
@@ -1546,6 +1561,37 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
}
#ifndef QT_NO_UDPSOCKET
+#ifndef QT_NO_NETWORKINTERFACE
+bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &,
+ const QNetworkInterface &)
+{
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Operation on socket is not supported"));
+ return false;
+}
+
+bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &,
+ const QNetworkInterface &)
+{
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Operation on socket is not supported"));
+ return false;
+}
+
+
+QNetworkInterface QSocks5SocketEngine::multicastInterface() const
+{
+ return QNetworkInterface();
+}
+
+bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
+{
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Operation on socket is not supported"));
+ return false;
+}
+#endif // QT_NO_NETWORKINTERFACE
+
qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr,
quint16 *port)
{
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
index 7c1b762..9b2f6e3 100644
--- a/src/network/socket/qsocks5socketengine_p.h
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -92,6 +92,15 @@ public:
qint64 write(const char *data, qint64 len);
#ifndef QT_NO_UDPSOCKET
+#ifndef QT_NO_NETWORKINTERFACE
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &interface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &interface);
+ QNetworkInterface multicastInterface() const;
+ bool setMulticastInterface(const QNetworkInterface &iface);
+#endif // QT_NO_NETWORKINTERFACE
+
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0,
quint16 *port = 0);
qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr,
diff --git a/src/network/socket/qsymbiansocketengine.cpp b/src/network/socket/qsymbiansocketengine.cpp
new file mode 100644
index 0000000..966af88
--- /dev/null
+++ b/src/network/socket/qsymbiansocketengine.cpp
@@ -0,0 +1,1782 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNATIVESOCKETENGINE_DEBUG
+#include "qsymbiansocketengine_p.h"
+
+#include "qiodevice.h"
+#include "qhostaddress.h"
+#include "qelapsedtimer.h"
+#include "qvarlengtharray.h"
+#include "qnetworkinterface.h"
+#include <private/qnetworksession_p.h>
+#include <es_sock.h>
+#include <in_sock.h>
+#include <net/if.h>
+
+#include <private/qcore_symbian_p.h>
+
+#if !defined(QT_NO_NETWORKPROXY)
+# include "qnetworkproxy.h"
+# include "qabstractsocket.h"
+# include "qtcpserver.h"
+#endif
+
+#include <QCoreApplication>
+
+#include <qabstracteventdispatcher.h>
+#include <private/qeventdispatcher_symbian_p.h>
+#include <qsocketnotifier.h>
+#include <qnetworkinterface.h>
+
+#include <private/qthread_p.h>
+#include <private/qobject_p.h>
+#include <private/qsystemerror_p.h>
+
+#if defined QNATIVESOCKETENGINE_DEBUG
+#include <qstring.h>
+#include <ctype.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define Q_VOID
+// Common constructs
+#define Q_CHECK_VALID_SOCKETLAYER(function, returnValue) do { \
+ if (!isValid()) { \
+ qWarning(""#function" was called on an uninitialized socket device"); \
+ return returnValue; \
+ } } while (0)
+#define Q_CHECK_INVALID_SOCKETLAYER(function, returnValue) do { \
+ if (isValid()) { \
+ qWarning(""#function" was called on an already initialized socket device"); \
+ return returnValue; \
+ } } while (0)
+#define Q_CHECK_STATE(function, checkState, returnValue) do { \
+ if (d->socketState != (checkState)) { \
+ qWarning(""#function" was not called in "#checkState); \
+ return (returnValue); \
+ } } while (0)
+#define Q_CHECK_NOT_STATE(function, checkState, returnValue) do { \
+ if (d->socketState == (checkState)) { \
+ qWarning(""#function" was called in "#checkState); \
+ return (returnValue); \
+ } } while (0)
+#define Q_CHECK_STATES(function, state1, state2, returnValue) do { \
+ if (d->socketState != (state1) && d->socketState != (state2)) { \
+ qWarning(""#function" was called" \
+ " not in "#state1" or "#state2); \
+ return (returnValue); \
+ } } while (0)
+#define Q_CHECK_TYPE(function, type, returnValue) do { \
+ if (d->socketType != (type)) { \
+ qWarning(#function" was called by a" \
+ " socket other than "#type""); \
+ return (returnValue); \
+ } } while (0)
+
+#if defined QNATIVESOCKETENGINE_DEBUG
+
+/*
+ Returns a human readable representation of the first \a len
+ characters in \a data.
+*/
+static QByteArray qt_prettyDebug(const char *data, int len, int maxSize)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len; ++i) {
+ char c = data[i];
+ if (isprint(c)) {
+ out += c;
+ } else switch (c) {
+ case '\n': out += "\\n"; break;
+ case '\r': out += "\\r"; break;
+ case '\t': out += "\\t"; break;
+ default:
+ QString tmp;
+ tmp.sprintf("\\%o", c);
+ out += tmp.toLatin1();
+ }
+ }
+
+ if (len < maxSize)
+ out += "...";
+
+ return out;
+}
+#endif
+
+void QSymbianSocketEnginePrivate::getPortAndAddress(const TInetAddr& a, quint16 *port, QHostAddress *addr)
+{
+ if (a.Family() == KAfInet6 && !a.IsV4Compat() && !a.IsV4Mapped()) {
+ Q_IPV6ADDR tmp;
+ memcpy(&tmp, a.Ip6Address().u.iAddr8, sizeof(tmp));
+ if (addr) {
+ QHostAddress tmpAddress;
+ tmpAddress.setAddress(tmp);
+ *addr = tmpAddress;
+ TPckgBuf<TSoInetIfQuery> query;
+ query().iSrcAddr = a;
+ TInt err = nativeSocket.GetOpt(KSoInetIfQueryBySrcAddr, KSolInetIfQuery, query);
+ if (!err)
+ addr->setScopeId(qt_TDesC2QString(query().iName));
+ else
+ addr->setScopeId(QString::number(a.Scope()));
+ }
+ if (port)
+ *port = a.Port();
+ return;
+ }
+ if (port)
+ *port = a.Port();
+ if (addr) {
+ QHostAddress tmpAddress;
+ tmpAddress.setAddress(a.Address());
+ *addr = tmpAddress;
+ }
+}
+/*! \internal
+
+ Creates and returns a new socket descriptor of type \a socketType
+ and \a socketProtocol. Returns -1 on failure.
+*/
+bool QSymbianSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType,
+ QAbstractSocket::NetworkLayerProtocol socketProtocol)
+{
+ Q_Q(QSymbianSocketEngine);
+ TUint family = KAfInet; // KAfInet6 is only used as an address family, not as a protocol family
+ TUint type = (socketType == QAbstractSocket::UdpSocket) ? KSockDatagram : KSockStream;
+ TUint protocol = (socketType == QAbstractSocket::UdpSocket) ? KProtocolInetUdp : KProtocolInetTcp;
+
+ //Check if there is a user specified session
+ QVariant v(q->property("_q_networksession"));
+ TInt err;
+ if (v.isValid()) {
+ QSharedPointer<QNetworkSession> s = qvariant_cast<QSharedPointer<QNetworkSession> >(v);
+ err = QNetworkSessionPrivate::nativeOpenSocket(*s, nativeSocket, family, type, protocol);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::createNewSocket - _q_networksession was set" << err;
+#endif
+ } else {
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::createNewSocket - _q_networksession was not set, using implicit connection";
+#endif
+ // using implicit connection allows localhost connections without starting any RConnection, see QTBUG-16155 and QTBUG-16843
+ // when a remote address is used, socket server will start the system default connection if there is no route.
+ err = nativeSocket.Open(socketServer, family, type, protocol);
+ }
+
+ if (err != KErrNone) {
+ switch (err) {
+ case KErrNotSupported:
+ case KErrNotFound:
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ ProtocolUnsupportedErrorString);
+ break;
+ default:
+ setError(err);
+ break;
+ }
+
+ return false;
+ }
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::createNewSocket - created" << nativeSocket.SubSessionHandle();
+#endif
+ socketDescriptor = QSymbianSocketManager::instance().addSocket(nativeSocket);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << " - allocated socket descriptor" << socketDescriptor;
+#endif
+ return true;
+}
+
+void QSymbianSocketEnginePrivate::setPortAndAddress(TInetAddr& nativeAddr, quint16 port, const QHostAddress &addr)
+{
+ nativeAddr.SetPort(port);
+ if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
+ TPckgBuf<TSoInetIfQuery> query;
+ query().iName = qt_QString2TPtrC(addr.scopeId());
+ TInt err = nativeSocket.GetOpt(KSoInetIfQueryByName, KSolInetIfQuery, query);
+ if (!err)
+ nativeAddr.SetScope(query().iIndex);
+ else
+ nativeAddr.SetScope(0);
+ Q_IPV6ADDR ip6 = addr.toIPv6Address();
+ TIp6Addr v6addr;
+ memcpy(v6addr.u.iAddr8, ip6.c, 16);
+ nativeAddr.SetAddress(v6addr);
+ } else if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
+ nativeAddr.SetAddress(addr.toIPv4Address());
+ } else {
+ qWarning("unsupported network protocol (%d)", addr.protocol());
+ }
+}
+
+QSymbianSocketEnginePrivate::QSymbianSocketEnginePrivate() :
+ socketDescriptor(-1),
+ socketServer(QSymbianSocketManager::instance().getSocketServer()),
+ readNotificationsEnabled(false),
+ writeNotificationsEnabled(false),
+ exceptNotificationsEnabled(false),
+ asyncSelect(0),
+ hasReceivedBufferedDatagram(false)
+{
+}
+
+QSymbianSocketEnginePrivate::~QSymbianSocketEnginePrivate()
+{
+ selectTimer.Close();
+}
+
+
+QSymbianSocketEngine::QSymbianSocketEngine(QObject *parent)
+ : QAbstractSocketEngine(*new QSymbianSocketEnginePrivate(), parent)
+{
+}
+
+
+QSymbianSocketEngine::~QSymbianSocketEngine()
+{
+ close();
+}
+
+/*!
+ Initializes a QSymbianSocketEngine by creating a new socket of type \a
+ socketType and network layer protocol \a protocol. Returns true on
+ success; otherwise returns false.
+
+ If the socket was already initialized, this function closes the
+ socket before reeinitializing it.
+
+ The new socket is non-blocking, and for UDP sockets it's also
+ broadcast enabled.
+*/
+bool QSymbianSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol protocol)
+{
+ Q_D(QSymbianSocketEngine);
+ if (isValid())
+ close();
+
+ // Create the socket
+ if (!d->createNewSocket(socketType, protocol)) {
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ QString typeStr = QLatin1String("UnknownSocketType");
+ if (socketType == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
+ else if (socketType == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
+ QString protocolStr = QLatin1String("UnknownProtocol");
+ if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
+ else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol");
+ qDebug("QSymbianSocketEngine::initialize(type == %s, protocol == %s) failed: %s",
+ typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(), d->socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+
+ // Make the socket nonblocking.
+ if (!setOption(NonBlockingSocketOption, 1)) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ d->NonBlockingInitFailedErrorString);
+ close();
+ return false;
+ }
+
+ // Set the broadcasting flag if it's a UDP socket.
+ if (socketType == QAbstractSocket::UdpSocket
+ && !setOption(BroadcastSocketOption, 1)) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ d->BroadcastingInitFailedErrorString);
+ close();
+ return false;
+ }
+
+
+ // Make sure we receive out-of-band data
+ if (socketType == QAbstractSocket::TcpSocket
+ && !setOption(ReceiveOutOfBandData, 1)) {
+ qWarning("QSymbianSocketEngine::initialize unable to inline out-of-band data");
+ }
+
+
+ d->socketType = socketType;
+ d->socketProtocol = protocol;
+ return true;
+}
+
+/*! \overload
+
+ Initializes the socket using \a socketDescriptor instead of
+ creating a new one. The socket type and network layer protocol are
+ determined automatically. The socket's state is set to \a
+ socketState.
+
+ If the socket type is either TCP or UDP, it is made non-blocking.
+ UDP sockets are also broadcast enabled.
+ */
+bool QSymbianSocketEngine::initialize(int socketDescriptor, QAbstractSocket::SocketState socketState)
+{
+ Q_D(QSymbianSocketEngine);
+
+ if (isValid())
+ close();
+
+ if (!QSymbianSocketManager::instance().lookupSocket(socketDescriptor, d->nativeSocket)) {
+ qWarning("QSymbianSocketEngine::initialize - socket descriptor not found");
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QSymbianSocketEnginePrivate::InvalidSocketErrorString);
+ return false;
+ }
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::initialize - attached to" << d->nativeSocket.SubSessionHandle() << socketDescriptor;
+#endif
+ Q_ASSERT(d->socketDescriptor == socketDescriptor || d->socketDescriptor == -1);
+ d->socketDescriptor = socketDescriptor;
+
+ // determine socket type and protocol
+ if (!d->fetchConnectionParameters()) {
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::initialize(socketDescriptor == %i) failed: %s",
+ socketDescriptor, d->socketErrorString.toLatin1().constData());
+#endif
+ d->socketDescriptor = -1;
+ return false;
+ }
+
+ if (d->socketType != QAbstractSocket::UnknownSocketType) {
+ // Make the socket nonblocking.
+ if (!setOption(NonBlockingSocketOption, 1)) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ d->NonBlockingInitFailedErrorString);
+ close();
+ return false;
+ }
+
+ // Set the broadcasting flag if it's a UDP socket.
+ if (d->socketType == QAbstractSocket::UdpSocket
+ && !setOption(BroadcastSocketOption, 1)) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ d->BroadcastingInitFailedErrorString);
+ close();
+ return false;
+ }
+
+ // Make sure we receive out-of-band data
+ if (d->socketType == QAbstractSocket::TcpSocket
+ && !setOption(ReceiveOutOfBandData, 1)) {
+ qWarning("QSymbianSocketEngine::initialize unable to inline out-of-band data");
+ }
+ }
+
+ d->socketState = socketState;
+ return true;
+}
+
+/*!
+ Returns true if the socket is valid; otherwise returns false. A
+ socket is valid if it has not been successfully initialized, or if
+ it has been closed.
+*/
+bool QSymbianSocketEngine::isValid() const
+{
+ Q_D(const QSymbianSocketEngine);
+ return d->socketDescriptor != -1;
+}
+
+
+/*!
+ Returns the native socket descriptor. Any use of this descriptor
+ stands the risk of being non-portable.
+*/
+int QSymbianSocketEngine::socketDescriptor() const
+{
+ Q_D(const QSymbianSocketEngine);
+ return d->socketDescriptor;
+}
+
+/*
+ Sets the socket option \a opt to \a v.
+*/
+bool QSymbianSocketEngine::setOption(QAbstractSocketEngine::SocketOption opt, int v)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setOption(), false);
+
+ TUint n = 0;
+ TUint level = KSOLSocket; // default
+
+ if (!QSymbianSocketEnginePrivate::translateSocketOption(opt, n, level))
+ return false;
+
+ if (!level && !n)
+ return true;
+
+ return (KErrNone == d->nativeSocket.SetOpt(n, level, v));
+}
+
+/*
+ Returns the value of the socket option \a opt.
+*/
+int QSymbianSocketEngine::option(QAbstractSocketEngine::SocketOption opt) const
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::option(), -1);
+
+ TUint n;
+ TUint level = KSOLSocket; // default
+
+ if (!QSymbianSocketEnginePrivate::translateSocketOption(opt, n, level))
+ return false;
+
+ if (!level && !n)
+ return 1;
+
+ int v = -1;
+ //GetOpt() is non const
+ TInt err = d->nativeSocket.GetOpt(n, level, v);
+ if (!err)
+ return v;
+
+ return -1;
+}
+
+bool QSymbianSocketEnginePrivate::translateSocketOption(QAbstractSocketEngine::SocketOption opt, TUint &n, TUint &level)
+{
+
+ switch (opt) {
+ case QAbstractSocketEngine::ReceiveBufferSocketOption:
+ n = KSORecvBuf;
+ break;
+ case QAbstractSocketEngine::SendBufferSocketOption:
+ n = KSOSendBuf;
+ break;
+ case QAbstractSocketEngine::NonBlockingSocketOption:
+ n = KSONonBlockingIO;
+ break;
+ case QAbstractSocketEngine::AddressReusable:
+ level = KSolInetIp;
+ n = KSoReuseAddr;
+ break;
+ case QAbstractSocketEngine::BroadcastSocketOption:
+ case QAbstractSocketEngine::BindExclusively:
+ level = 0;
+ n = 0;
+ return true;
+ case QAbstractSocketEngine::ReceiveOutOfBandData:
+ level = KSolInetTcp;
+ n = KSoTcpOobInline;
+ break;
+ case QAbstractSocketEngine::LowDelayOption:
+ level = KSolInetTcp;
+ n = KSoTcpNoDelay;
+ break;
+ case QAbstractSocketEngine::KeepAliveOption:
+ level = KSolInetTcp;
+ n = KSoTcpKeepAlive;
+ break;
+ case QAbstractSocketEngine::MulticastLoopbackOption:
+ level = KSolInetIp;
+ n = KSoIp6MulticastLoop;
+ break;
+ case QAbstractSocketEngine::MulticastTtlOption:
+ level = KSolInetIp;
+ n = KSoIp6MulticastHops;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+qint64 QSymbianSocketEngine::receiveBufferSize() const
+{
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::receiveBufferSize(), -1);
+ return option(ReceiveBufferSocketOption);
+}
+
+void QSymbianSocketEngine::setReceiveBufferSize(qint64 size)
+{
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setReceiveBufferSize(), Q_VOID);
+ setOption(ReceiveBufferSocketOption, size);
+}
+
+qint64 QSymbianSocketEngine::sendBufferSize() const
+{
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setSendBufferSize(), -1);
+ return option(SendBufferSocketOption);
+}
+
+void QSymbianSocketEngine::setSendBufferSize(qint64 size)
+{
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setSendBufferSize(), Q_VOID);
+ setOption(SendBufferSocketOption, size);
+}
+
+/*!
+ Connects to the remote host name given by \a name on port \a
+ port. When this function is called, the upper-level will not
+ perform a hostname lookup.
+
+ The native socket engine does not support this operation,
+ but some other socket engines (notably proxy-based ones) do.
+*/
+bool QSymbianSocketEngine::connectToHostByName(const QString &name, quint16 port)
+{
+ Q_UNUSED(name);
+ Q_UNUSED(port);
+ Q_D(QSymbianSocketEngine);
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QSymbianSocketEnginePrivate::OperationUnsupportedErrorString);
+ return false;
+}
+
+/*!
+ If there's a connection activity on the socket, process it. Then
+ notify our parent if there really was activity.
+*/
+void QSymbianSocketEngine::connectionComplete()
+{
+ Q_D(QSymbianSocketEngine);
+ Q_ASSERT(state() == QAbstractSocket::ConnectingState);
+
+ // as it was a non blocking connect, call again to find the result.
+ connectToHost(d->peerAddress, d->peerPort);
+ if (state() != QAbstractSocket::ConnectingState) {
+ // we changed states
+ QAbstractSocketEngine::connectionNotification();
+ }
+}
+
+
+bool QSymbianSocketEngine::connectToHost(const QHostAddress &addr, quint16 port)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::connectToHost(), false);
+
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug("QSymbianSocketEngine::connectToHost() : %d ", d->socketDescriptor);
+#endif
+
+ if (!d->checkProxy(addr))
+ return false;
+
+ d->peerAddress = addr;
+ d->peerPort = port;
+
+ TInetAddr nativeAddr;
+ d->setPortAndAddress(nativeAddr, port, addr);
+ TRequestStatus status;
+ d->nativeSocket.Connect(nativeAddr, status);
+ User::WaitForRequest(status);
+ TInt err = status.Int();
+ //For non blocking connect, KErrAlreadyExists is returned from the second Connect() to indicate
+ //the connection is up. So treat this the same as KErrNone which would be returned from the first
+ //call if it wouldn't block. (e.g. winsock wrapper in the emulator ignores the nonblocking flag)
+ if (err && err != KErrAlreadyExists) {
+ switch (err) {
+ case KErrWouldBlock:
+ d->socketState = QAbstractSocket::ConnectingState;
+ break;
+ default:
+ d->setError(err);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+
+ if (d->socketState != QAbstractSocket::ConnectedState) {
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::connectToHost(%s, %i) == false (%s)",
+ addr.toString().toLatin1().constData(), port,
+ d->socketState == QAbstractSocket::ConnectingState
+ ? "Connection in progress" : d->socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::Connect(%s, %i) == true",
+ addr.toString().toLatin1().constData(), port);
+#endif
+
+ d->socketState = QAbstractSocket::ConnectedState;
+ d->fetchConnectionParameters();
+ return true;
+}
+
+bool QSymbianSocketEngine::bind(const QHostAddress &address, quint16 port)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::bind(), false);
+
+ if (!d->checkProxy(address))
+ return false;
+
+ Q_CHECK_STATE(QSymbianSocketEngine::bind(), QAbstractSocket::UnconnectedState, false);
+
+ TInetAddr nativeAddr;
+ if (address == QHostAddress::Any || address == QHostAddress::AnyIPv6) {
+ //Should allow both IPv4 and IPv6
+ //Listening on "0.0.0.0" accepts ONLY ipv4 connections
+ //Listening on "::" accepts ONLY ipv6 connections
+ nativeAddr.SetFamily(KAFUnspec);
+ nativeAddr.SetPort(port);
+ } else {
+ d->setPortAndAddress(nativeAddr, port, address);
+ }
+
+ TInt err = d->nativeSocket.Bind(nativeAddr);
+#ifdef __WINS__
+ if (err == KErrArgument) // winsock prt returns wrong error code
+ err = KErrInUse;
+#endif
+
+ if (err) {
+ switch (err) {
+ case KErrNotFound:
+ // the specified interface was not found - use the error code expected
+ d->setError(QAbstractSocket::SocketAddressNotAvailableError, QSymbianSocketEnginePrivate::AddressNotAvailableErrorString);
+ break;
+ default:
+ d->setError(err);
+ break;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::bind(%s, %i) == false (%s)",
+ address.toString().toLatin1().constData(), port, d->socketErrorString.toLatin1().constData());
+#endif
+
+ return false;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::bind(%s, %i) == true",
+ address.toString().toLatin1().constData(), port);
+#endif
+ d->socketState = QAbstractSocket::BoundState;
+
+ d->fetchConnectionParameters();
+
+ // When we bind to unspecified address (to get a dual mode socket), report back the
+ // same type of address that was requested. This is required for SOCKS proxy to work.
+ if (nativeAddr.Family() == KAFUnspec)
+ d->localAddress = address;
+ return true;
+}
+
+bool QSymbianSocketEngine::listen()
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::listen(), false);
+ Q_CHECK_STATE(QSymbianSocketEngine::listen(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
+ TInt err = d->nativeSocket.Listen(50);
+ if (err) {
+ d->setError(err);
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::listen() == false (%s)",
+ d->socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::listen() == true");
+#endif
+
+ d->socketState = QAbstractSocket::ListeningState;
+ return true;
+}
+
+int QSymbianSocketEngine::accept()
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::accept(), -1);
+ Q_CHECK_STATE(QSymbianSocketEngine::accept(), QAbstractSocket::ListeningState, false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::accept(), QAbstractSocket::TcpSocket, false);
+ RSocket blankSocket;
+ blankSocket.Open(d->socketServer);
+ TRequestStatus status;
+ d->nativeSocket.Accept(blankSocket, status);
+ User::WaitForRequest(status);
+ if (status.Int()) {
+ blankSocket.Close();
+ if (status != KErrWouldBlock)
+ qWarning("QSymbianSocketEngine::accept() - error %d", status.Int());
+ return -1;
+ }
+
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::accept - created" << blankSocket.SubSessionHandle();
+#endif
+ int fd = QSymbianSocketManager::instance().addSocket(blankSocket);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << " - allocated socket descriptor" << fd;
+#endif
+ return fd;
+}
+
+qint64 QSymbianSocketEngine::bytesAvailable() const
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::bytesAvailable(), -1);
+ Q_CHECK_NOT_STATE(QSymbianSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, false);
+ int nbytes = 0;
+ qint64 available = 0;
+ TInt err = d->nativeSocket.GetOpt(KSOReadBytesPending, KSOLSocket, nbytes);
+ if (err)
+ return 0;
+ available = (qint64) nbytes;
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::bytesAvailable() == %lli", available);
+#endif
+ return available;
+}
+
+bool QSymbianSocketEngine::hasPendingDatagrams() const
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::hasPendingDatagrams(), false);
+ Q_CHECK_NOT_STATE(QSymbianSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false);
+ int nbytes;
+ TInt err = d->nativeSocket.GetOpt(KSOReadBytesPending,KSOLSocket, nbytes);
+ return err == KErrNone && nbytes > 0;
+}
+
+qint64 QSymbianSocketEngine::pendingDatagramSize() const
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::pendingDatagramSize(), false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false);
+ //can only buffer one datagram at a time
+ if (d->hasReceivedBufferedDatagram)
+ return d->receivedDataBuffer.size();
+ int nbytes = 0;
+ TInt err = d->nativeSocket.GetOpt(KSOReadBytesPending,KSOLSocket, nbytes);
+ if (nbytes > 0) {
+ //nbytes includes IP header, which is of variable length (IPv4 with or without options, IPv6...)
+ //therefore read the datagram into a buffer to find its true size
+ d->receivedDataBuffer.resize(nbytes);
+ TPtr8 buffer((TUint8*)d->receivedDataBuffer.data(), nbytes);
+ //nbytes = size including IP header, buffer is a pointer descriptor backed by the receivedDataBuffer
+ TInetAddr addr;
+ TRequestStatus status;
+ //RecvFrom copies only the payload (we don't want the header so don't specify the option to retrieve it)
+ d->nativeSocket.RecvFrom(buffer, addr, 0, status);
+ User::WaitForRequest(status);
+ if (status != KErrNone) {
+ d->receivedDataBuffer.clear();
+ return 0;
+ }
+ nbytes = buffer.Length();
+ //nbytes = size of payload, resize the receivedDataBuffer to the final size
+ d->receivedDataBuffer.resize(nbytes);
+ d->hasReceivedBufferedDatagram = true;
+ //now receivedDataBuffer contains one datagram, which has been removed from the socket's internal buffer
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug() << "QSymbianSocketEngine::pendingDatagramSize buffering" << nbytes << "bytes";
+#endif
+ }
+ return qint64(nbytes);
+}
+
+
+qint64 QSymbianSocketEngine::readDatagram(char *data, qint64 maxSize,
+ QHostAddress *address, quint16 *port)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::readDatagram(), -1);
+ Q_CHECK_TYPE(QSymbianSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, false);
+
+ // if a datagram was buffered in pendingDatagramSize(), return it now
+ if (d->hasReceivedBufferedDatagram) {
+ qint64 size = qMin(maxSize, (qint64)d->receivedDataBuffer.size());
+ memcpy(data, d->receivedDataBuffer.constData(), size);
+ d->receivedDataBuffer.clear();
+ d->hasReceivedBufferedDatagram = false;
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug() << "QSymbianSocketEngine::readDatagram returning" << size << "bytes from buffer";
+#endif
+ return size;
+ }
+
+ TPtr8 buffer((TUint8*)data, (int)maxSize);
+ TInetAddr addr;
+ TRequestStatus status;
+ d->nativeSocket.RecvFrom(buffer, addr, 0, status);
+ User::WaitForRequest(status); //Non blocking receive
+
+ if (status.Int()) {
+ d->setError(QAbstractSocket::NetworkError, d->ReceiveDatagramErrorString);
+ } else if (port || address) {
+ d->getPortAndAddress(addr, port, address);
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ int len = buffer.Length();
+ qDebug("QSymbianSocketEngine::receiveDatagram(%p \"%s\", %lli, %s, %i) == %lli",
+ data, qt_prettyDebug(data, qMin(len, ssize_t(16)), len).data(), maxSize,
+ address ? address->toString().toLatin1().constData() : "(nil)",
+ port ? *port : 0, (qint64) len);
+#endif
+
+ if (status.Int())
+ return -1;
+ return qint64(buffer.Length());
+}
+
+
+qint64 QSymbianSocketEngine::writeDatagram(const char *data, qint64 len,
+ const QHostAddress &host, quint16 port)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::writeDatagram(), -1);
+ Q_CHECK_TYPE(QSymbianSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1);
+ TPtrC8 buffer((TUint8*)data, (int)len);
+ TInetAddr addr;
+ d->setPortAndAddress(addr, port, host);
+ TSockXfrLength sentBytes;
+ TRequestStatus status;
+ d->nativeSocket.SendTo(buffer, addr, 0, status, sentBytes);
+ User::WaitForRequest(status); //Non blocking send
+ TInt err = status.Int();
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::writeDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli (err=%d)", data,
+ qt_prettyDebug(data, qMin<int>(len, 16), len).data(), len, host.toString().toLatin1().constData(),
+ port, (qint64) sentBytes(), err);
+#endif
+
+ if (err) {
+ switch (err) {
+ case KErrWouldBlock:
+ // do not error the socket. (otherwise socket layer is reset)
+ // On symbian^1 and earlier, KErrWouldBlock is returned when interface is not up yet
+ // On symbian^3, KErrNone is returned but sentBytes = 0
+ return 0;
+ case KErrTooBig:
+ d->setError(QAbstractSocket::DatagramTooLargeError, d->DatagramTooLargeErrorString);
+ break;
+ default:
+ d->setError(QAbstractSocket::NetworkError, d->SendDatagramErrorString);
+ }
+ return -1;
+ }
+
+ if (QSysInfo::s60Version() <= QSysInfo::SV_S60_5_0) {
+ // This is evil hack, but for some reason native RSocket::SendTo returns 0,
+ // for large datagrams (such as 600 bytes). Based on comments from Open C team
+ // this should happen only in platforms <= S60 5.0.
+ return len;
+ }
+ return sentBytes();
+}
+
+bool QSymbianSocketEnginePrivate::fetchConnectionParameters()
+{
+ localPort = 0;
+ localAddress.clear();
+ peerPort = 0;
+ peerAddress.clear();
+
+ if (socketDescriptor == -1)
+ return false;
+
+ if (!nativeSocket.SubSessionHandle()) {
+ if (!QSymbianSocketManager::instance().lookupSocket(socketDescriptor, nativeSocket)) {
+ setError(QAbstractSocket::UnsupportedSocketOperationError, InvalidSocketErrorString);
+ return false;
+ }
+ }
+
+ // Determine local address
+ TSockAddr addr;
+ nativeSocket.LocalName(addr);
+ getPortAndAddress(addr, &localPort, &localAddress);
+
+ // Determine protocol family
+ socketProtocol = localAddress.protocol();
+
+ // Determine the remote address
+ nativeSocket.RemoteName(addr);
+ getPortAndAddress(addr, &peerPort, &peerAddress);
+
+ // Determine the socket type (UDP/TCP)
+ TProtocolDesc protocol;
+ TInt err = nativeSocket.Info(protocol);
+ if (err) {
+ setError(err);
+ return false;
+ } else {
+ switch (protocol.iProtocol) {
+ case KProtocolInetTcp:
+ socketType = QAbstractSocket::TcpSocket;
+ break;
+ case KProtocolInetUdp:
+ socketType = QAbstractSocket::UdpSocket;
+ break;
+ default:
+ socketType = QAbstractSocket::UnknownSocketType;
+ break;
+ }
+ }
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ QString socketProtocolStr = QLatin1String("UnknownProtocol");
+ if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = QLatin1String("IPv4Protocol");
+ else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = QLatin1String("IPv6Protocol");
+
+ QString socketTypeStr = QLatin1String("UnknownSocketType");
+ if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QLatin1String("TcpSocket");
+ else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QLatin1String("UdpSocket");
+
+ qDebug("QSymbianSocketEnginePrivate::fetchConnectionParameters() local == %s:%i,"
+ " peer == %s:%i, socket == %s - %s",
+ localAddress.toString().toLatin1().constData(), localPort,
+ peerAddress.toString().toLatin1().constData(), peerPort,socketTypeStr.toLatin1().constData(),
+ socketProtocolStr.toLatin1().constData());
+#endif
+ return true;
+}
+
+void QSymbianSocketEngine::close()
+{
+ if (!isValid())
+ return;
+ Q_D(QSymbianSocketEngine);
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::close()");
+#endif
+
+ d->readNotificationsEnabled = false;
+ d->writeNotificationsEnabled = false;
+ d->exceptNotificationsEnabled = false;
+ if (d->asyncSelect) {
+ d->asyncSelect->deleteLater();
+ d->asyncSelect = 0;
+ }
+
+ //RSocket::Shutdown(EImmediate) performs a fast disconnect. For TCP,
+ //this would mean sending RST rather than FIN so we don't do that.
+ //Qt's disconnectFromHost() API doesn't expose this choice.
+ //RSocket::Close will internally do a normal shutdown of the socket.
+ if (d->socketType == QAbstractSocket::UdpSocket) {
+ //RSocket::Close has been observed to block for a long time with
+ //UDP sockets. Doing an immediate shutdown first works around this
+ //problem. Since UDP is connectionless, there should be no difference
+ //at the network interface.
+ TRequestStatus stat;
+ d->nativeSocket.Shutdown(RSocket::EImmediate, stat);
+ User::WaitForRequest(stat);
+ }
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::close - closing socket" << d->nativeSocket.SubSessionHandle() << d->socketDescriptor;
+#endif
+ //remove must come before close to avoid a race where another thread gets the old subsession handle
+ //reused & asserts when calling QSymbianSocketManager::instance->addSocket
+ QSymbianSocketManager::instance().removeSocket(d->nativeSocket);
+ d->nativeSocket.Close();
+ d->socketDescriptor = -1;
+
+ d->socketState = QAbstractSocket::UnconnectedState;
+ d->hasSetSocketError = false;
+ d->localPort = 0;
+ d->localAddress.clear();
+ d->peerPort = 0;
+ d->peerAddress.clear();
+
+ d->hasReceivedBufferedDatagram = false;
+ d->receivedDataBuffer.clear();
+}
+
+qint64 QSymbianSocketEngine::write(const char *data, qint64 len)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::write(), -1);
+ Q_CHECK_STATE(QSymbianSocketEngine::write(), QAbstractSocket::ConnectedState, -1);
+ TPtrC8 buffer((TUint8*)data, (int)len);
+ TSockXfrLength sentBytes = 0;
+ TRequestStatus status;
+ d->nativeSocket.Send(buffer, 0, status, sentBytes);
+ User::WaitForRequest(status);
+ TInt err = status.Int();
+
+ if (err) {
+ switch (err) {
+ case KErrDisconnected:
+ case KErrEof:
+ sentBytes = -1;
+ d->setError(QAbstractSocket::RemoteHostClosedError, d->RemoteHostClosedErrorString);
+ close();
+ break;
+ case KErrTooBig:
+ d->setError(QAbstractSocket::DatagramTooLargeError, d->DatagramTooLargeErrorString);
+ break;
+ case KErrWouldBlock:
+ break;
+ default:
+ sentBytes = -1;
+ d->setError(err);
+ close();
+ break;
+ }
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::write(%p \"%s\", %llu) == %i",
+ data, qt_prettyDebug(data, qMin((int) len, 16),
+ (int) len).data(), len, (int) sentBytes());
+#endif
+
+ return qint64(sentBytes());
+}
+/*
+*/
+qint64 QSymbianSocketEngine::read(char *data, qint64 maxSize)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::read(), -1);
+ Q_CHECK_STATES(QSymbianSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1);
+
+ // if a datagram was buffered in pendingDatagramSize(), return it now
+ if (d->hasReceivedBufferedDatagram) {
+ qint64 size = qMin(maxSize, (qint64)d->receivedDataBuffer.size());
+ memcpy(data, d->receivedDataBuffer.constData(), size);
+ d->receivedDataBuffer.clear();
+ d->hasReceivedBufferedDatagram = false;
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug() << "QSymbianSocketEngine::read returning" << size << "bytes from buffer";
+#endif
+ return size;
+ }
+
+ TPtr8 buffer((TUint8*)data, (int)maxSize);
+ TSockXfrLength received = 0;
+ TRequestStatus status;
+ TSockAddr dummy;
+ if (d->socketType == QAbstractSocket::UdpSocket) {
+ //RecvOneOrMore() can only be used with stream-interfaced connected sockets; datagram interface sockets will return KErrNotSupported.
+ d->nativeSocket.RecvFrom(buffer, dummy, 0, status);
+ } else {
+ d->nativeSocket.RecvOneOrMore(buffer, 0, status, received);
+ }
+ User::WaitForRequest(status); //Non blocking receive
+ TInt err = status.Int();
+ int r = buffer.Length();
+
+ if (err == KErrWouldBlock) {
+ // No data was available for reading
+ r = -2;
+ } else if (err != KErrNone) {
+ d->setError(err);
+ close();
+ r = -1;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QSymbianSocketEngine::read(%p \"%s\", %llu) == %i (err = %d)",
+ data, qt_prettyDebug(data, qMin(r, ssize_t(16)), r).data(),
+ maxSize, r, err);
+#endif
+
+ return qint64(r);
+}
+
+int QSymbianSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+{
+ bool readyRead = false;
+ bool readyWrite = false;
+ if (selectForRead)
+ return nativeSelect(timeout, true, false, &readyRead, &readyWrite);
+ else
+ return nativeSelect(timeout, false, true, &readyRead, &readyWrite);
+}
+
+/*!
+ \internal
+ \param timeout timeout in milliseconds
+ \param checkRead caller is interested if the socket is ready to read
+ \param checkWrite caller is interested if the socket is ready for write
+ \param selectForRead (out) should set to true if ready to read
+ \param selectForWrite (out) should set to true if ready to write
+ \return 0 on timeout, >0 on success, <0 on error
+ */
+int QSymbianSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const
+{
+ //cancel asynchronous notifier (only one IOCTL allowed at a time)
+ if (asyncSelect)
+ asyncSelect->Cancel();
+
+ TPckgBuf<TUint> selectFlags;
+ selectFlags() = KSockSelectExcept;
+ if (checkRead)
+ selectFlags() |= KSockSelectRead;
+ if (checkWrite)
+ selectFlags() |= KSockSelectWrite;
+ TInt err;
+ if (timeout == 0) {
+ //if timeout is zero, poll
+ err = nativeSocket.GetOpt(KSOSelectPoll, KSOLSocket, selectFlags);
+ } else {
+ TRequestStatus selectStat;
+ nativeSocket.Ioctl(KIOctlSelect, selectStat, &selectFlags, KSOLSocket);
+
+ if (timeout < 0)
+ User::WaitForRequest(selectStat); //negative means no timeout
+ else {
+ if (!selectTimer.Handle())
+ qt_symbian_throwIfError(selectTimer.CreateLocal());
+ TRequestStatus timerStat;
+ selectTimer.HighRes(timerStat, timeout * 1000);
+ User::WaitForRequest(timerStat, selectStat);
+ if (selectStat == KRequestPending) {
+ nativeSocket.CancelIoctl();
+ //CancelIoctl completes the request (most likely with KErrCancel)
+ //We need to wait for this to keep the thread semaphore balanced (or active scheduler will panic)
+ User::WaitForRequest(selectStat);
+ //restart asynchronous notifier (only one IOCTL allowed at a time)
+ if (asyncSelect)
+ asyncSelect->IssueRequest();
+ #ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::nativeSelect: select timeout";
+ #endif
+ return 0; //timeout
+ } else {
+ selectTimer.Cancel();
+ User::WaitForRequest(timerStat);
+ }
+ }
+
+ #ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::nativeSelect: select status" << selectStat.Int() << (int)selectFlags();
+ #endif
+ err = selectStat.Int();
+ }
+
+ if (!err && (selectFlags() & KSockSelectExcept)) {
+ nativeSocket.GetOpt(KSOSelectLastError, KSOLSocket, err);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEnginePrivate::nativeSelect: select last error" << err;
+#endif
+ }
+ if (err) {
+ //set the error here, because read won't always return the same error again as select.
+ const_cast<QSymbianSocketEnginePrivate*>(this)->setError(err);
+ //restart asynchronous notifier (only one IOCTL allowed at a time)
+ if (asyncSelect)
+ asyncSelect->IssueRequest();
+ return err;
+ }
+ if (checkRead && (selectFlags() & KSockSelectRead)) {
+ Q_ASSERT(selectForRead);
+ *selectForRead = true;
+ }
+ if (checkWrite && (selectFlags() & KSockSelectWrite)) {
+ Q_ASSERT(selectForWrite);
+ *selectForWrite = true;
+ }
+ //restart asynchronous notifier (only one IOCTL allowed at a time)
+ if (asyncSelect)
+ asyncSelect->IssueRequest();
+ return 1;
+}
+
+bool QSymbianSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::joinMulticastGroup(), false);
+ Q_CHECK_STATE(QSymbianSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false);
+ return d->multicastGroupMembershipHelper(groupAddress, iface, KSoIp6JoinGroup);
+}
+
+bool QSymbianSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::leaveMulticastGroup(), false);
+ Q_CHECK_STATE(QSymbianSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false);
+ return d->multicastGroupMembershipHelper(groupAddress, iface, KSoIp6LeaveGroup);
+}
+
+bool QSymbianSocketEnginePrivate::multicastGroupMembershipHelper(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface,
+ TUint operation)
+{
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug() << "QSymbianSocketEnginePrivate::multicastGroupMembershipHelper" << groupAddress << iface << operation;
+#endif
+ //translate address
+ TPckgBuf<TIp6Mreq> option;
+ if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ Q_IPV6ADDR ip6 = groupAddress.toIPv6Address();
+ memcpy(option().iAddr.u.iAddr8, ip6.c, 16);
+ } else {
+ TInetAddr wrapped;
+ wrapped.SetAddress(groupAddress.toIPv4Address());
+ wrapped.ConvertToV4Mapped();
+ option().iAddr = wrapped.Ip6Address();
+ }
+ option().iInterface = iface.index();
+ //join or leave group
+ TInt err = nativeSocket.SetOpt(operation, KSolInetIp, option);
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug() << "address" << qt_prettyDebug((const char *)(option().iAddr.u.iAddr8), 16, 16);
+ qDebug() << "interface" << option().iInterface;
+ qDebug() << "error" << err;
+#endif
+ if (err) {
+ setError(err);
+ }
+ return (KErrNone == err);
+}
+
+QNetworkInterface QSymbianSocketEngine::multicastInterface() const
+{
+ //### symbian 3 has no API equivalent to this
+ const Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::multicastInterface(), QNetworkInterface());
+ Q_CHECK_TYPE(QSymbianSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface());
+ return QNetworkInterface();
+}
+
+bool QSymbianSocketEngine::setMulticastInterface(const QNetworkInterface &iface)
+{
+ //### symbian 3 has no API equivalent to this
+ //this is possibly a unix'ism as the RConnection on which the socket was created is probably controlling this
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setMulticastInterface(), false);
+ Q_CHECK_TYPE(QSymbianSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false);
+ return false;
+}
+
+bool QSymbianSocketEnginePrivate::checkProxy(const QHostAddress &address)
+{
+ if (address == QHostAddress::LocalHost || address == QHostAddress::LocalHostIPv6)
+ return true;
+
+#if !defined(QT_NO_NETWORKPROXY)
+ QObject *parent = q_func()->parent();
+ QNetworkProxy proxy;
+ if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(parent)) {
+ proxy = socket->proxy();
+ } else if (QTcpServer *server = qobject_cast<QTcpServer *>(parent)) {
+ proxy = server->proxy();
+ } else {
+ // no parent -> no proxy
+ return true;
+ }
+
+ if (proxy.type() == QNetworkProxy::DefaultProxy)
+ proxy = QNetworkProxy::applicationProxy();
+
+ if (proxy.type() != QNetworkProxy::DefaultProxy &&
+ proxy.type() != QNetworkProxy::NoProxy) {
+ // QSymbianSocketEngine doesn't do proxies
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ InvalidProxyTypeString);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+// ### this is also in QNativeSocketEngine, unify it
+/*! \internal
+
+ Sets the error and error string if not set already. The only
+ interesting error is the first one that occurred, and not the last
+ one.
+*/
+void QSymbianSocketEnginePrivate::setError(QAbstractSocket::SocketError error, ErrorString errorString) const
+{
+ if (hasSetSocketError) {
+ // Only set socket errors once for one engine; expect the
+ // 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))
+ hasSetSocketError = true;
+
+ socketError = error;
+
+ switch (errorString) {
+ case NonBlockingInitFailedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unable to initialize non-blocking socket");
+ break;
+ case BroadcastingInitFailedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unable to initialize broadcast socket");
+ break;
+ case NoIpV6ErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support");
+ break;
+ case RemoteHostClosedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("The remote host closed the connection");
+ break;
+ case TimeOutErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Network operation timed out");
+ break;
+ case ResourceErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Out of resources");
+ break;
+ case OperationUnsupportedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unsupported socket operation");
+ break;
+ case ProtocolUnsupportedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Protocol type not supported");
+ break;
+ case InvalidSocketErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Invalid socket descriptor");
+ break;
+ case HostUnreachableErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Host unreachable");
+ break;
+ case NetworkUnreachableErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Network unreachable");
+ break;
+ case AccessErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Permission denied");
+ break;
+ case ConnectionTimeOutErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Connection timed out");
+ break;
+ case ConnectionRefusedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Connection refused");
+ break;
+ case AddressInuseErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("The bound address is already in use");
+ break;
+ case AddressNotAvailableErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("The address is not available");
+ break;
+ case AddressProtectedErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("The address is protected");
+ break;
+ case DatagramTooLargeErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Datagram was too large to send");
+ break;
+ case SendDatagramErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unable to send a message");
+ break;
+ case ReceiveDatagramErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unable to receive a message");
+ break;
+ case WriteErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unable to write");
+ break;
+ case ReadErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Network error");
+ break;
+ case PortInuseErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Another socket is already listening on the same port");
+ break;
+ case NotSocketErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Operation on non-socket");
+ break;
+ case InvalidProxyTypeString:
+ socketErrorString = QSymbianSocketEngine::tr("The proxy type is invalid for this operation");
+ break;
+ case InvalidAddressErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("The address is invalid for this operation");
+ break;
+ case SessionNotOpenErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("The specified network session is not opened");
+ break;
+ case UnknownSocketErrorString:
+ socketErrorString = QSymbianSocketEngine::tr("Unknown error");
+ break;
+ }
+}
+
+void QSymbianSocketEnginePrivate::setError(TInt symbianError)
+{
+ switch (symbianError) {
+ case KErrDisconnected:
+ case KErrEof:
+ case KErrConnectionTerminated: //interface stopped externally - RConnection::Stop(EStopAuthoritative)
+ setError(QAbstractSocket::RemoteHostClosedError,
+ QSymbianSocketEnginePrivate::RemoteHostClosedErrorString);
+ break;
+ case KErrNetUnreach:
+ setError(QAbstractSocket::NetworkError,
+ QSymbianSocketEnginePrivate::NetworkUnreachableErrorString);
+ break;
+ case KErrHostUnreach:
+ setError(QAbstractSocket::NetworkError,
+ QSymbianSocketEnginePrivate::HostUnreachableErrorString);
+ break;
+ case KErrNoProtocolOpt:
+ setError(QAbstractSocket::NetworkError,
+ QSymbianSocketEnginePrivate::ProtocolUnsupportedErrorString);
+ break;
+ case KErrInUse:
+ setError(QAbstractSocket::AddressInUseError, AddressInuseErrorString);
+ break;
+ case KErrPermissionDenied:
+ setError(QAbstractSocket::SocketAccessError, AccessErrorString);
+ break;
+ case KErrNotSupported:
+ setError(QAbstractSocket::UnsupportedSocketOperationError, OperationUnsupportedErrorString);
+ break;
+ case KErrNoMemory:
+ setError(QAbstractSocket::SocketResourceError, ResourceErrorString);
+ break;
+ case KErrCouldNotConnect:
+ setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
+ break;
+ case KErrTimedOut:
+ setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
+ break;
+ case KErrBadName:
+ setError(QAbstractSocket::NetworkError, InvalidAddressErrorString);
+ break;
+ default:
+ socketError = QAbstractSocket::NetworkError;
+ socketErrorString = QSystemError(symbianError, QSystemError::NativeError).toString();
+ break;
+ }
+ hasSetSocketError = true;
+}
+
+void QSymbianSocketEngine::startNotifications()
+{
+ Q_D(QSymbianSocketEngine);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::startNotifications" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled;
+#endif
+ if (!d->asyncSelect && (d->readNotificationsEnabled || d->writeNotificationsEnabled || d->exceptNotificationsEnabled)) {
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::startNotifications(), Q_VOID);
+ if (d->threadData->eventDispatcher) {
+ d->asyncSelect = q_check_ptr(new QAsyncSelect(
+ static_cast<QEventDispatcherSymbian*> (d->threadData->eventDispatcher), d->nativeSocket,
+ this));
+ } else {
+ // call again when event dispatcher has been created
+ QMetaObject::invokeMethod(this, "startNotifications", Qt::QueuedConnection);
+ }
+ }
+ if (d->asyncSelect)
+ d->asyncSelect->IssueRequest();
+}
+
+bool QSymbianSocketEngine::isReadNotificationEnabled() const
+{
+ Q_D(const QSymbianSocketEngine);
+ return d->readNotificationsEnabled;
+}
+
+void QSymbianSocketEngine::setReadNotificationEnabled(bool enable)
+{
+ Q_D(QSymbianSocketEngine);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::setReadNotificationEnabled" << enable << "socket" << d->socketDescriptor;
+#endif
+ d->readNotificationsEnabled = enable;
+ startNotifications();
+}
+
+bool QSymbianSocketEngine::isWriteNotificationEnabled() const
+{
+ Q_D(const QSymbianSocketEngine);
+ return d->writeNotificationsEnabled;
+}
+
+void QSymbianSocketEngine::setWriteNotificationEnabled(bool enable)
+{
+ Q_D(QSymbianSocketEngine);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::setWriteNotificationEnabled" << enable << "socket" << d->socketDescriptor;
+#endif
+ d->writeNotificationsEnabled = enable;
+ startNotifications();
+}
+
+bool QSymbianSocketEngine::isExceptionNotificationEnabled() const
+{
+ Q_D(const QSymbianSocketEngine);
+ return d->exceptNotificationsEnabled;
+ return false;
+}
+
+void QSymbianSocketEngine::setExceptionNotificationEnabled(bool enable)
+{
+ Q_D(QSymbianSocketEngine);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::setExceptionNotificationEnabled" << enable << "socket" << d->socketDescriptor;
+#endif
+ d->exceptNotificationsEnabled = enable;
+ startNotifications();
+}
+
+bool QSymbianSocketEngine::waitForRead(int msecs, bool *timedOut)
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::waitForRead(), false);
+ Q_CHECK_NOT_STATE(QSymbianSocketEngine::waitForRead(),
+ QAbstractSocket::UnconnectedState, false);
+
+ if (timedOut)
+ *timedOut = false;
+
+ int ret = d->nativeSelect(msecs, true);
+ if (ret == 0) {
+ if (timedOut)
+ *timedOut = true;
+ d->setError(QAbstractSocket::SocketTimeoutError,
+ d->TimeOutErrorString);
+ d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
+ return false;
+ } else if (state() == QAbstractSocket::ConnectingState) {
+ connectToHost(d->peerAddress, d->peerPort);
+ }
+
+ return ret > 0;
+}
+
+bool QSymbianSocketEngine::waitForWrite(int msecs, bool *timedOut)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::waitForWrite(), false);
+ Q_CHECK_NOT_STATE(QSymbianSocketEngine::waitForWrite(),
+ QAbstractSocket::UnconnectedState, false);
+
+ if (timedOut)
+ *timedOut = false;
+
+ int ret = d->nativeSelect(msecs, false);
+
+ if (ret == 0) {
+ if (timedOut)
+ *timedOut = true;
+ d->setError(QAbstractSocket::SocketTimeoutError,
+ d->TimeOutErrorString);
+ d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
+ return false;
+ } else if (state() == QAbstractSocket::ConnectingState) {
+ connectToHost(d->peerAddress, d->peerPort);
+ }
+
+ return ret > 0;
+}
+
+bool QSymbianSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs, bool *timedOut)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::waitForWrite(), false);
+ Q_CHECK_NOT_STATE(QSymbianSocketEngine::waitForReadOrWrite(),
+ QAbstractSocket::UnconnectedState, false);
+
+ int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite);
+
+ if (ret == 0) {
+ if (timedOut)
+ *timedOut = true;
+ d->setError(QAbstractSocket::SocketTimeoutError,
+ d->TimeOutErrorString);
+ d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
+ return false;
+ } else if (state() == QAbstractSocket::ConnectingState) {
+ connectToHost(d->peerAddress, d->peerPort);
+ }
+
+ return ret > 0;
+}
+
+qint64 QSymbianSocketEngine::bytesToWrite() const
+{
+ // This is what the QNativeSocketEngine does
+ return 0;
+}
+
+bool QSymbianSocketEngine::event(QEvent* ev)
+{
+ Q_D(QSymbianSocketEngine);
+ if (ev->type() == QEvent::ThreadChange) {
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::event - ThreadChange" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled;
+#endif
+ if (d->asyncSelect) {
+ delete d->asyncSelect;
+ d->asyncSelect = 0;
+ // recreate select in new thread (because it is queued, the method is called in the new thread context)
+ QMetaObject::invokeMethod(this, "startNotifications", Qt::QueuedConnection);
+ }
+ d->selectTimer.Close();
+ return true;
+ }
+ return QAbstractSocketEngine::event(ev);
+}
+
+QAsyncSelect::QAsyncSelect(QEventDispatcherSymbian *dispatcher, RSocket& sock, QSymbianSocketEngine *parent)
+ : QActiveObject(CActive::EPriorityStandard, dispatcher),
+ m_inSocketEvent(false),
+ m_deleteLater(false),
+ m_socket(sock),
+ m_selectFlags(0),
+ engine(parent)
+{
+ CActiveScheduler::Add(this);
+}
+
+QAsyncSelect::~QAsyncSelect()
+{
+ Cancel();
+}
+
+void QAsyncSelect::DoCancel()
+{
+ m_socket.CancelIoctl();
+}
+
+void QAsyncSelect::RunL()
+{
+ QT_TRYCATCH_LEAVING(run());
+}
+
+//RunError is called by the active scheduler if RunL leaves.
+//Typically this will happen if a std::bad_alloc propagates down from the application
+TInt QAsyncSelect::RunError(TInt aError)
+{
+ if (engine) {
+ QT_TRY {
+ engine->d_func()->setError(aError);
+ if (engine->isExceptionNotificationEnabled())
+ engine->exceptionNotification();
+ if (engine->isReadNotificationEnabled())
+ engine->readNotification();
+ }
+ QT_CATCH(...) {}
+ }
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QAsyncSelect::RunError" << aError;
+#endif
+ return KErrNone;
+}
+
+void QAsyncSelect::run()
+{
+ //when event loop disabled socket events, defer until later
+ if (maybeDeferSocketEvent())
+ return;
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug() << "QAsyncSelect::run" << m_selectBuf() << m_selectFlags;
+#endif
+ m_inSocketEvent = true;
+ m_selectBuf() &= m_selectFlags; //the select ioctl reports everything, so mask to only what we requested
+ //KSockSelectReadContinuation is for reading datagrams in a mode that doesn't discard when the
+ //datagram is larger than the read buffer - Qt doesn't need to use this.
+ if (engine && engine->isReadNotificationEnabled()
+ && ((m_selectBuf() & KSockSelectRead) || iStatus != KErrNone)) {
+ engine->readNotification();
+ }
+ if (engine && engine->isWriteNotificationEnabled()
+ && ((m_selectBuf() & KSockSelectWrite) || iStatus != KErrNone)) {
+ if (engine->state() == QAbstractSocket::ConnectingState)
+ engine->connectionComplete();
+ else
+ engine->writeNotification();
+ }
+ if (engine && engine->isExceptionNotificationEnabled()
+ && ((m_selectBuf() & KSockSelectExcept) || iStatus != KErrNone)) {
+ engine->exceptionNotification();
+ }
+ m_inSocketEvent = false;
+ if (m_deleteLater) {
+ delete this;
+ return;
+ }
+ // select again (unless disabled by one of the callbacks)
+ IssueRequest();
+}
+
+void QAsyncSelect::deleteLater()
+{
+ if (m_inSocketEvent) {
+ engine = 0;
+ m_deleteLater = true;
+ } else {
+ delete this;
+ }
+}
+
+void QAsyncSelect::IssueRequest()
+{
+ if (m_inSocketEvent)
+ return; //prevent thrashing during a callback - socket engine enables/disables multiple notifiers
+ TUint selectFlags = 0;
+ if (engine->isReadNotificationEnabled())
+ selectFlags |= KSockSelectRead;
+ if (engine->isWriteNotificationEnabled())
+ selectFlags |= KSockSelectWrite;
+ if (engine->isExceptionNotificationEnabled())
+ selectFlags |= KSockSelectExcept;
+ if (selectFlags != m_selectFlags) {
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QAsyncSelect::IssueRequest() - select flags" << m_selectFlags << "->" << selectFlags;
+#endif
+ Cancel();
+ m_selectFlags = selectFlags;
+ }
+ if (m_selectFlags && !IsActive()) {
+ //always request errors (write notification does not complete on connect errors)
+ m_selectBuf() = m_selectFlags | KSockSelectExcept;
+ m_socket.Ioctl(KIOctlSelect, iStatus, &m_selectBuf, KSOLSocket);
+ SetActive();
+ }
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QAsyncSelect::IssueRequest() - IsActive" << IsActive();
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qsymbiansocketengine_p.h b/src/network/socket/qsymbiansocketengine_p.h
new file mode 100644
index 0000000..aad6228
--- /dev/null
+++ b/src/network/socket/qsymbiansocketengine_p.h
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSYMBIANSOCKETENGINE_P_H
+#define QSYMBIANSOCKETENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "QtNetwork/qhostaddress.h"
+#include "private/qabstractsocketengine_p.h"
+#include "qplatformdefs.h"
+
+#include <private/qeventdispatcher_symbian_p.h>
+#include <unistd.h>
+#include <es_sock.h>
+#include <in_sock.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QSymbianSocketEnginePrivate;
+class QNetworkInterface;
+
+class Q_AUTOTEST_EXPORT QSymbianSocketEngine : public QAbstractSocketEngine
+{
+ Q_OBJECT
+ friend class QAsyncSelect;
+public:
+ QSymbianSocketEngine(QObject *parent = 0);
+ ~QSymbianSocketEngine();
+
+ bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol);
+ bool initialize(int socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState);
+
+ int socketDescriptor() const;
+
+ bool isValid() const;
+
+ bool connectToHost(const QHostAddress &address, quint16 port);
+ bool connectToHostByName(const QString &name, quint16 port);
+ bool bind(const QHostAddress &address, quint16 port);
+ bool listen();
+ int accept();
+ void close();
+
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ QNetworkInterface multicastInterface() const;
+ bool setMulticastInterface(const QNetworkInterface &iface);
+
+ qint64 bytesAvailable() const;
+
+ qint64 read(char *data, qint64 maxlen);
+ qint64 write(const char *data, qint64 len);
+
+ qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0,
+ quint16 *port = 0);
+ qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr,
+ quint16 port);
+ bool hasPendingDatagrams() const;
+ qint64 pendingDatagramSize() const;
+
+ qint64 bytesToWrite() const;
+
+ qint64 receiveBufferSize() const;
+ void setReceiveBufferSize(qint64 bufferSize);
+
+ qint64 sendBufferSize() const;
+ void setSendBufferSize(qint64 bufferSize);
+
+ int option(SocketOption option) const;
+ bool setOption(SocketOption option, int value);
+
+ bool waitForRead(int msecs = 30000, bool *timedOut = 0);
+ bool waitForWrite(int msecs = 30000, bool *timedOut = 0);
+ bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs = 30000, bool *timedOut = 0);
+
+ bool isReadNotificationEnabled() const;
+ void setReadNotificationEnabled(bool enable);
+ bool isWriteNotificationEnabled() const;
+ void setWriteNotificationEnabled(bool enable);
+ bool isExceptionNotificationEnabled() const;
+ void setExceptionNotificationEnabled(bool enable);
+
+ bool event(QEvent* ev);
+
+ Q_INVOKABLE void startNotifications();
+
+ void connectionComplete();
+
+private:
+ Q_DECLARE_PRIVATE(QSymbianSocketEngine)
+ Q_DISABLE_COPY(QSymbianSocketEngine)
+};
+
+class QSocketNotifier;
+
+class QReadNotifier;
+class QWriteNotifier;
+class QExceptionNotifier;
+class QAsyncSelect : public QActiveObject
+{
+public:
+ QAsyncSelect(QEventDispatcherSymbian *dispatcher, RSocket& sock, QSymbianSocketEngine *parent);
+ ~QAsyncSelect();
+
+ void deleteLater();
+ void IssueRequest();
+
+ void refresh();
+
+protected:
+ void DoCancel();
+ void RunL();
+ void run();
+ TInt RunError(TInt aError);
+
+private:
+ bool m_inSocketEvent;
+ bool m_deleteLater;
+ RSocket &m_socket;
+
+ TUint m_selectFlags;
+ TPckgBuf<TUint> m_selectBuf; //in & out IPC buffer
+ QSymbianSocketEngine *engine;
+};
+
+class QSymbianSocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QSymbianSocketEngine)
+public:
+ QSymbianSocketEnginePrivate();
+ ~QSymbianSocketEnginePrivate();
+
+ int socketDescriptor;
+ mutable RSocket nativeSocket;
+ // From QtCore:
+ RSocketServ& socketServer;
+ mutable RTimer selectTimer;
+
+ bool readNotificationsEnabled;
+ bool writeNotificationsEnabled;
+ bool exceptNotificationsEnabled;
+ QAsyncSelect* asyncSelect;
+
+ mutable QByteArray receivedDataBuffer;
+ mutable bool hasReceivedBufferedDatagram;
+ enum ErrorString {
+ NonBlockingInitFailedErrorString,
+ BroadcastingInitFailedErrorString,
+ NoIpV6ErrorString,
+ RemoteHostClosedErrorString,
+ TimeOutErrorString,
+ ResourceErrorString,
+ OperationUnsupportedErrorString,
+ ProtocolUnsupportedErrorString,
+ InvalidSocketErrorString,
+ HostUnreachableErrorString,
+ NetworkUnreachableErrorString,
+ AccessErrorString,
+ ConnectionTimeOutErrorString,
+ ConnectionRefusedErrorString,
+ AddressInuseErrorString,
+ AddressNotAvailableErrorString,
+ AddressProtectedErrorString,
+ DatagramTooLargeErrorString,
+ SendDatagramErrorString,
+ ReceiveDatagramErrorString,
+ WriteErrorString,
+ ReadErrorString,
+ PortInuseErrorString,
+ NotSocketErrorString,
+ InvalidProxyTypeString,
+ //symbian specific
+ InvalidAddressErrorString,
+ SessionNotOpenErrorString,
+
+ UnknownSocketErrorString = -1
+ };
+ void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
+
+ void getPortAndAddress(const TInetAddr& a, quint16 *port, QHostAddress *addr);
+ void setPortAndAddress(TInetAddr& nativeAddr, quint16 port, const QHostAddress &addr);
+ void setError(TInt symbianError);
+
+ int nativeSelect(int timeout, bool selectForRead) const;
+ int nativeSelect(int timeout, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const;
+
+ bool createNewSocket(QAbstractSocket::SocketType socketType,
+ QAbstractSocket::NetworkLayerProtocol socketProtocol);
+
+ bool checkProxy(const QHostAddress &address);
+ bool fetchConnectionParameters();
+
+ bool multicastGroupMembershipHelper(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface,
+ TUint operation);
+ static bool translateSocketOption(QAbstractSocketEngine::SocketOption opt, TUint &n, TUint &level);
+};
+
+QT_END_NAMESPACE
+
+#endif // QSYMBIANSOCKETENGINE_P_H
diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp
index 5a52084..ccdc50e 100644
--- a/src/network/socket/qtcpserver.cpp
+++ b/src/network/socket/qtcpserver.cpp
@@ -105,7 +105,7 @@
#include "qhostaddress.h"
#include "qlist.h"
#include "qpointer.h"
-#include "qnativesocketengine_p.h"
+#include "qabstractsocketengine_p.h"
#include "qtcpserver.h"
#include "qtcpsocket.h"
#include "qnetworkproxy.h"
@@ -292,6 +292,10 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port)
d->serverSocketErrorString = tr("Operation on socket is not supported");
return false;
}
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket engine (if it has been set)
+ d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
+#endif
if (!d->socketEngine->initialize(QAbstractSocket::TcpSocket, proto)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
@@ -412,6 +416,15 @@ bool QTcpServer::setSocketDescriptor(int socketDescriptor)
if (d->socketEngine)
delete d->socketEngine;
d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);
+ if (!d->socketEngine) {
+ d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError;
+ d->serverSocketErrorString = tr("Operation on socket is not supported");
+ return false;
+ }
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the socket engine (if it has been set)
+ d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
+#endif
if (!d->socketEngine->initialize(socketDescriptor, QAbstractSocket::ListeningState)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
index e3480b1..6bc93dd 100644
--- a/src/network/socket/qudpsocket.cpp
+++ b/src/network/socket/qudpsocket.cpp
@@ -77,14 +77,23 @@
\snippet doc/src/snippets/code/src_network_socket_qudpsocket.cpp 0
+ QUdpSocket also supports UDP multicast. Use joinMulticastGroup() and
+ leaveMulticastGroup() to control group membership, and
+ QAbstractSocket::MulticastTtlOption and
+ QAbstractSocket::MulticastLoopbackOption to set the TTL and loopback socket
+ options. Use setMulticastInterface() to control the outgoing interface for
+ multicast datagrams, and multicastInterface() to query it.
+
With QUdpSocket, you can also establish a virtual connection to a
UDP server using connectToHost() and then use read() and write()
to exchange datagrams without specifying the receiver for each
datagram.
- The \l{network/broadcastsender}{Broadcast Sender} and
- \l{network/broadcastreceiver}{Broadcast Receiver} examples
- illustrate how to use QUdpSocket in applications.
+ The \l{network/broadcastsender}{Broadcast Sender},
+ \l{network/broadcastreceiver}{Broadcast Receiver},
+ \l{network/multicastsender}{Multicast Sender}, and
+ \l{network/multicastreceiver}{Multicast Receiver} examples illustrate how
+ to use QUdpSocket in applications.
\section1 Symbian Platform Security Requirements
@@ -145,6 +154,7 @@
*/
#include "qhostaddress.h"
+#include "qnetworkinterface.h"
#include "qabstractsocket_p.h"
#include "qudpsocket.h"
@@ -192,7 +202,7 @@ bool QUdpSocketPrivate::doEnsureInitialized(const QHostAddress &bindAddress, qui
#endif
// now check if the socket engine is initialized and to the right type
- if (!socketEngine || !socketEngine->isValid() || socketEngine->protocol() != proto) {
+ if (!socketEngine || !socketEngine->isValid()) {
resolveProxy(remoteAddress.toString(), bindPort);
if (!initSocketLayer(address->protocol()))
return false;
@@ -328,6 +338,116 @@ bool QUdpSocket::bind(quint16 port, BindMode mode)
return bind(QHostAddress::Any, port, mode);
}
+#ifndef QT_NO_NETWORKINTERFACE
+
+/*!
+ \since 4.8
+
+ Joins the the multicast group specified by \a groupAddress on the default
+ interface chosen by the operating system. The socket must be in BoundState,
+ otherwise an error occurs.
+
+ This function returns true if successful; otherwise it returns false
+ and sets the socket error accordingly.
+
+ \sa leaveMulticastGroup()
+*/
+bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress)
+{
+ return joinMulticastGroup(groupAddress, QNetworkInterface());
+}
+
+/*!
+ \since 4.8
+ \overload
+
+ Joins the multicast group address \a groupAddress on the interface \a
+ iface.
+
+ \sa leaveMulticastGroup()
+*/
+bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ Q_D(QUdpSocket);
+ QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false);
+ return d->socketEngine->joinMulticastGroup(groupAddress, iface);
+}
+
+/*!
+ \since 4.8
+
+ Leaves the multicast group specified by \a groupAddress on the default
+ interface chosen by the operating system. The socket must be in BoundState,
+ otherwise an error occurs.
+
+ This function returns true if successful; otherwise it returns false and
+ sets the socket error accordingly.
+
+ \sa joinMulticastGroup()
+*/
+bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress)
+{
+ return leaveMulticastGroup(groupAddress, QNetworkInterface());
+}
+
+/*!
+ \since 4.8
+ \overload
+
+ Leaves the multicast group specified by \a groupAddress on the interface \a
+ iface.
+
+ \sa joinMulticastGroup()
+*/
+bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface)
+{
+ QT_CHECK_BOUND("QUdpSocket::leaveMulticastGroup()", false);
+ return d_func()->socketEngine->leaveMulticastGroup(groupAddress, iface);
+}
+
+/*!
+ \since 4.8
+
+ Returns the interface for the outgoing interface for multicast datagrams.
+ This corresponds to the IP_MULTICAST_IF socket option for IPv4 sockets and
+ the IPV6_MULTICAST_IF socket option for IPv6 sockets. If no interface has
+ been previously set, this function returns an invalid QNetworkInterface.
+ The socket must be in BoundState, otherwise an invalid QNetworkInterface is
+ returned.
+
+ \sa setMulticastInterface()
+*/
+QNetworkInterface QUdpSocket::multicastInterface() const
+{
+ Q_D(const QUdpSocket);
+ QT_CHECK_BOUND("QUdpSocket::multicastInterface()", QNetworkInterface());
+ return d->socketEngine->multicastInterface();
+}
+
+/*!
+ \since 4.8
+
+ Sets the outgoing interface for multicast datagrams to the interface \a
+ iface. This corresponds to the IP_MULTICAST_IF socket option for IPv4
+ sockets and the IPV6_MULTICAST_IF socket option for IPv6 sockets. The
+ socket must be in BoundState, otherwise this function does nothing.
+
+ \sa multicastInterface(), joinMulticastGroup(), leaveMulticastGroup()
+*/
+void QUdpSocket::setMulticastInterface(const QNetworkInterface &iface)
+{
+ Q_D(QUdpSocket);
+ if (!isValid()) {
+ qWarning("QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState");
+ return;
+ }
+ d->socketEngine->setMulticastInterface(iface);
+}
+
+#endif // QT_NO_NETWORKINTERFACE
+
/*!
Returns true if at least one datagram is waiting to be read;
otherwise returns false.
@@ -388,16 +508,6 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre
return -1;
qint64 sent = d->socketEngine->writeDatagram(data, size, address, port);
-#ifdef Q_OS_SYMBIAN
- if( QSysInfo::s60Version() <= QSysInfo::SV_S60_5_0 ) {
- // This is evil hack, but for some reason native RSocket::SendTo returns 0,
- // for large datagrams (such as 600 bytes). Based on comments from Open C team
- // this should happen only in platforms <= S60 5.0.
- // As an workaround, we just set sent = size
- if( sent == 0 )
- sent = size;
- }
-#endif
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
if (sent >= 0) {
diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h
index aafef5c..c477abd 100644
--- a/src/network/socket/qudpsocket.h
+++ b/src/network/socket/qudpsocket.h
@@ -53,6 +53,7 @@ QT_MODULE(Network)
#ifndef QT_NO_UDPSOCKET
+class QNetworkInterface;
class QUdpSocketPrivate;
class Q_NETWORK_EXPORT QUdpSocket : public QAbstractSocket
@@ -76,6 +77,18 @@ public:
bool bind(quint16 port, BindMode mode);
// ### Qt 5: Merge the bind functions
+#ifndef QT_NO_NETWORKINTERFACE
+ bool joinMulticastGroup(const QHostAddress &groupAddress);
+ bool joinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress);
+ bool leaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+
+ QNetworkInterface multicastInterface() const;
+ void setMulticastInterface(const QNetworkInterface &iface);
+#endif
+
bool hasPendingDatagrams() const;
qint64 pendingDatagramSize() const;
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = 0, quint16 *port = 0);
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
index 2bafe13..ac90012 100644
--- a/src/network/socket/socket.pri
+++ b/src/network/socket/socket.pri
@@ -1,7 +1,6 @@
# Qt network socket
HEADERS += socket/qabstractsocketengine_p.h \
- socket/qnativesocketengine_p.h \
socket/qhttpsocketengine_p.h \
socket/qsocks5socketengine_p.h \
socket/qabstractsocket.h \
@@ -15,7 +14,6 @@ HEADERS += socket/qabstractsocketengine_p.h \
socket/qlocalsocket_p.h
SOURCES += socket/qabstractsocketengine.cpp \
- socket/qnativesocketengine.cpp \
socket/qhttpsocketengine.cpp \
socket/qsocks5socketengine.cpp \
socket/qabstractsocket.cpp \
@@ -25,9 +23,26 @@ SOURCES += socket/qabstractsocketengine.cpp \
socket/qlocalsocket.cpp \
socket/qlocalserver.cpp
-unix:SOURCES += socket/qnativesocketengine_unix.cpp \
+# On Symbian we use QSymbianSocketEngine
+symbian:SOURCES += socket/qsymbiansocketengine.cpp
+symbian:HEADERS += socket/qsymbiansocketengine_p.h
+# On others we use QNativeSocketEngine
+!symbian:SOURCES += socket/qnativesocketengine.cpp
+!symbian:HEADERS += socket/qnativesocketengine_p.h
+
+unix:!symbian: {
+ SOURCES += socket/qnativesocketengine_unix.cpp \
socket/qlocalsocket_unix.cpp \
socket/qlocalserver_unix.cpp
+}
+
+symbian: {
+ SOURCES += socket/qlocalsocket_tcp.cpp \
+ socket/qlocalserver_tcp.cpp
+
+ DEFINES += QT_LOCALSOCKET_TCP
+}
+
unix:HEADERS += \
socket/qnet_unix_p.h
@@ -43,3 +58,13 @@ wince*: {
DEFINES += QT_LOCALSOCKET_TCP
}
+
+integrity: {
+ SOURCES -= socket/qlocalsocket_unix.cpp \
+ socket/qlocalserver_unix.cpp
+ SOURCES += socket/qlocalsocket_tcp.cpp \
+ socket/qlocalserver_tcp.cpp \
+ socket/qnativesocketengine_unix.cpp
+
+ DEFINES += QT_LOCALSOCKET_TCP
+}
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 341ec8f..586c894 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -101,12 +101,17 @@ QT_BEGIN_NAMESPACE
Describes the protocol of the cipher.
- \value SslV3 SSLv3 - the default protocol.
+ \value SslV3 SSLv3
\value SslV2 SSLv2
\value TlsV1 TLSv1
\value UnknownProtocol The cipher's protocol cannot be determined.
\value AnyProtocol The socket understands SSLv2, SSLv3, and TLSv1. This
value is used by QSslSocket only.
+ \value TlsV1SslV3 On the client side, this will send
+ a TLS 1.0 Client Hello, enabling TLSv1 and SSLv3 connections.
+ On the server side, this will enable both SSLv3 and TLSv1 connections.
+ \value SecureProtocols The default option, using protocols known to be secure;
+ currently behaves like TlsV1SslV3.
Note: most servers using SSL understand both versions (2 and 3),
but it is recommended to use the latest version only for security
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index b77053b..2ecd1c3 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -75,8 +75,10 @@ namespace QSsl {
enum SslProtocol {
SslV3,
SslV2,
- TlsV1,
+ TlsV1, // ### Qt 5: rename to TlsV1_0 or so
AnyProtocol,
+ TlsV1SslV3,
+ SecureProtocols,
UnknownProtocol = -1
};
}
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 1ae98f4..2a2ad55 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -127,7 +127,7 @@
QT_BEGIN_NAMESPACE
// forward declaration
-static QMap<QString, QString> _q_mapFromOnelineName(char *name);
+static QMap<QString, QString> _q_mapFromX509Name(X509_NAME *name);
/*!
Constructs a QSslCertificate by reading \a format encoded data
@@ -324,7 +324,7 @@ QString QSslCertificate::issuerInfo(SubjectInfo info) const
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
- _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
+ _q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
return d->issuerInfo.value(_q_SubjectInfoToString(info));
}
@@ -341,7 +341,7 @@ QString QSslCertificate::issuerInfo(const QByteArray &tag) const
// lazy init
if (d->issuerInfo.isEmpty() && d->x509)
d->issuerInfo =
- _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(d->x509), 0, 0));
+ _q_mapFromX509Name(q_X509_get_issuer_name(d->x509));
return d->issuerInfo.value(QString::fromLatin1(tag));
}
@@ -360,7 +360,7 @@ QString QSslCertificate::subjectInfo(SubjectInfo info) const
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
- _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
+ _q_mapFromX509Name(q_X509_get_subject_name(d->x509));
return d->subjectInfo.value(_q_SubjectInfoToString(info));
}
@@ -376,7 +376,7 @@ QString QSslCertificate::subjectInfo(const QByteArray &tag) const
// lazy init
if (d->subjectInfo.isEmpty() && d->x509)
d->subjectInfo =
- _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(d->x509), 0, 0));
+ _q_mapFromX509Name(q_X509_get_subject_name(d->x509));
return d->subjectInfo.value(QString::fromLatin1(tag));
}
@@ -666,37 +666,17 @@ QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::Encodi
return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
}
-static QMap<QString, QString> _q_mapFromOnelineName(char *name)
+static QMap<QString, QString> _q_mapFromX509Name(X509_NAME *name)
{
QMap<QString, QString> info;
- QString infoStr = QString::fromLocal8Bit(name);
- q_CRYPTO_free(name);
-
- // ### The right-hand encoding seems to allow hex (Regulierungsbeh\xC8orde)
- //entry.replace(QLatin1String("\\x"), QLatin1String("%"));
- //entry = QUrl::fromPercentEncoding(entry.toLatin1());
- // ### See RFC-4630 for more details!
-
- QRegExp rx(QLatin1String("/([A-Za-z]+)=(.+)"));
-
- int pos = 0;
- while ((pos = rx.indexIn(infoStr, pos)) != -1) {
- const QString name = rx.cap(1);
-
- QString value = rx.cap(2);
- const int valuePos = rx.pos(2);
-
- const int next = rx.indexIn(value);
- if (next == -1) {
- info.insert(name, value);
- break;
- }
-
- value = value.left(next);
- info.insert(name, value);
- pos = valuePos + value.length();
+ for (int i = 0; i < q_X509_NAME_entry_count(name); ++i) {
+ X509_NAME_ENTRY *e = q_X509_NAME_get_entry(name, i);
+ const char *obj = q_OBJ_nid2sn(q_OBJ_obj2nid(q_X509_NAME_ENTRY_get_object(e)));
+ unsigned char *data = 0;
+ int size = q_ASN1_STRING_to_UTF8(&data, q_X509_NAME_ENTRY_get_data(e));
+ info[QString::fromUtf8(obj)] = QString::fromUtf8((char*)data, size);
+ q_CRYPTO_free(data);
}
-
return info;
}
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index a1f6918..69d3b66 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -47,18 +47,6 @@
QT_BEGIN_NAMESPACE
-template<> void QSharedDataPointer<QSslConfigurationPrivate>::detach()
-{
- if (d && d->ref == 1)
- return;
- QSslConfigurationPrivate *x = (d ? new QSslConfigurationPrivate(*d)
- : new QSslConfigurationPrivate);
- x->ref.ref();
- if (d && !d->ref.deref())
- delete d;
- d = x;
-}
-
/*!
\class QSslConfiguration
\brief The QSslConfiguration class holds the configuration and state of an SSL connection
@@ -126,7 +114,7 @@ template<> void QSharedDataPointer<QSslConfigurationPrivate>::detach()
Once any setter methods are called, isNull() will return false.
*/
QSslConfiguration::QSslConfiguration()
- : d(0)
+ : d(new QSslConfigurationPrivate)
{
}
@@ -176,7 +164,7 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->privateKey == other.d->privateKey &&
d->sessionCipher == other.d->sessionCipher &&
d->ciphers == other.d->ciphers &&
- d->caCertificates == d->caCertificates &&
+ d->caCertificates == other.d->caCertificates &&
d->protocol == other.d->protocol &&
d->peerVerifyMode == other.d->peerVerifyMode &&
d->peerVerifyDepth == other.d->peerVerifyDepth;
@@ -203,7 +191,15 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
*/
bool QSslConfiguration::isNull() const
{
- return d == 0;
+ return (d->protocol == QSsl::SecureProtocols &&
+ d->peerVerifyMode == QSslSocket::AutoVerifyPeer &&
+ d->peerVerifyDepth == 0 &&
+ d->caCertificates.count() == 0 &&
+ d->ciphers.count() == 0 &&
+ d->localCertificate.isNull() &&
+ d->privateKey.isNull() &&
+ d->peerCertificate.isNull() &&
+ d->peerCertificateChain.count() == 0);
}
/*!
@@ -213,7 +209,7 @@ bool QSslConfiguration::isNull() const
*/
QSsl::SslProtocol QSslConfiguration::protocol() const
{
- return d ? d->protocol : QSsl::SslV3;
+ return d->protocol;
}
/*!
@@ -237,13 +233,13 @@ void QSslConfiguration::setProtocol(QSsl::SslProtocol protocol)
client), and whether it should require that this certificate is valid.
The default mode is AutoVerifyPeer, which tells QSslSocket to use
- VerifyPeer for clients, QueryPeer for clients.
+ VerifyPeer for clients, QueryPeer for servers.
\sa setPeerVerifyMode()
*/
QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const
{
- return d ? d->peerVerifyMode : QSslSocket::AutoVerifyPeer;
+ return d->peerVerifyMode;
}
/*!
@@ -253,7 +249,7 @@ QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const
client), and whether it should require that this certificate is valid.
The default mode is AutoVerifyPeer, which tells QSslSocket to use
- VerifyPeer for clients, QueryPeer for clients.
+ VerifyPeer for clients, QueryPeer for servers.
\sa peerVerifyMode()
*/
@@ -276,7 +272,7 @@ void QSslConfiguration::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode)
*/
int QSslConfiguration::peerVerifyDepth() const
{
- return d ? d->peerVerifyDepth : 0;
+ return d->peerVerifyDepth;
}
/*!
@@ -307,7 +303,7 @@ void QSslConfiguration::setPeerVerifyDepth(int depth)
*/
QSslCertificate QSslConfiguration::localCertificate() const
{
- return d ? d->localCertificate : QSslCertificate();
+ return d->localCertificate;
}
/*!
@@ -361,7 +357,7 @@ void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate)
*/
QSslCertificate QSslConfiguration::peerCertificate() const
{
- return d ? d->peerCertificate : QSslCertificate();
+ return d->peerCertificate;
}
/*!
@@ -393,7 +389,7 @@ QSslCertificate QSslConfiguration::peerCertificate() const
*/
QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const
{
- return d ? d->peerCertificateChain : QList<QSslCertificate>();
+ return d->peerCertificateChain;
}
/*!
@@ -411,7 +407,7 @@ QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const
*/
QSslCipher QSslConfiguration::sessionCipher() const
{
- return d ? d->sessionCipher : QSslCipher();
+ return d->sessionCipher;
}
/*!
@@ -422,7 +418,7 @@ QSslCipher QSslConfiguration::sessionCipher() const
*/
QSslKey QSslConfiguration::privateKey() const
{
- return d ? d->privateKey : QSslKey();
+ return d->privateKey;
}
/*!
@@ -464,7 +460,7 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key)
*/
QList<QSslCipher> QSslConfiguration::ciphers() const
{
- return d ? d->ciphers : QList<QSslCipher>();
+ return d->ciphers;
}
/*!
@@ -494,7 +490,7 @@ void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
*/
QList<QSslCertificate> QSslConfiguration::caCertificates() const
{
- return d ? d->caCertificates : QList<QSslCertificate>();
+ return d->caCertificates;
}
/*!
@@ -518,7 +514,7 @@ void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certific
\list
\o no local certificate and no private key
- \o protocol SSLv3
+ \o protocol SecureProtocols (meaning either TLS 1.0 or SSL 3 will be used)
\o the system's default CA certificate list
\o the cipher list equal to the list of the SSL libraries'
supported SSL ciphers
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 6f8ddbd..258b454 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -86,7 +86,7 @@ public:
inline bool operator!=(const QSslConfiguration &other) const
{ return !(*this == other); }
- bool isNull() const;
+ bool isNull() const; // ### Qt 5: remove; who would need this?
QSsl::SslProtocol protocol() const;
void setProtocol(QSsl::SslProtocol protocol);
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index fcb00b2..af80e4c 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -80,7 +80,7 @@ class QSslConfigurationPrivate: public QSharedData
{
public:
QSslConfigurationPrivate()
- : protocol(QSsl::SslV3),
+ : protocol(QSsl::SecureProtocols),
peerVerifyMode(QSslSocket::AutoVerifyPeer),
peerVerifyDepth(0)
{ }
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index f9fa90b..5091ed2 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -86,6 +86,7 @@
\value HostNameMismatch
\value UnspecifiedError
\value NoSslSupport
+ \value CertificateBlacklisted
\sa QSslError::errorString()
*/
@@ -281,6 +282,9 @@ QString QSslError::errorString() const
break;
case NoSslSupport:
break;
+ case CertificateBlacklisted:
+ errStr = QSslSocket::tr("The peer certificate is blacklisted");
+ break;
default:
errStr = QSslSocket::tr("Unknown error");
break;
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
index 54cc4c1..451c95f 100644
--- a/src/network/ssl/qsslerror.h
+++ b/src/network/ssl/qsslerror.h
@@ -83,6 +83,7 @@ public:
NoPeerCertificate,
HostNameMismatch,
NoSslSupport,
+ CertificateBlacklisted,
UnspecifiedError = -1
};
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 80ce5bc..df61fb6 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -56,7 +56,7 @@
QSslSocket establishes a secure, encrypted TCP connection you can
use for transmitting encrypted data. It can operate in both client
and server mode, and it supports modern SSL protocols, including
- SSLv3 and TLSv1. By default, QSslSocket uses SSLv3, but you can
+ SSLv3 and TLSv1. By default, QSslSocket uses TLSv1, but you can
change the SSL protocol by calling setProtocol() as long as you do
it before the handshake has started.
@@ -143,6 +143,15 @@
setDefaultCaCertificates().
\endlist
+ \note If available, root certificates on Unix (excluding Mac OS X) will be
+ loaded on demand from the standard certificate directories. If
+ you do not want to load root certificates on demand, you need to call either
+ the static function setDefaultCaCertificates() before the first SSL handshake
+ is made in your application, (e.g. via
+ "QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());"),
+ or call setCaCertificates() on your QSslSocket instance prior to the SSL
+ handshake.
+
For more information about ciphers and certificates, refer to QSslCipher and
QSslCertificate.
@@ -543,7 +552,7 @@ bool QSslSocket::isEncrypted() const
}
/*!
- Returns the socket's SSL protocol. By default, \l QSsl::SslV3 is used.
+ Returns the socket's SSL protocol. By default, \l QSsl::SecureProtocols is used.
\sa setProtocol()
*/
@@ -650,6 +659,34 @@ void QSslSocket::setPeerVerifyDepth(int depth)
}
/*!
+ \since 4.8
+
+ Returns the different hostname for the certificate validation, as set by
+ setPeerVerifyName or by connectToHostEncrypted.
+
+ \sa setPeerVerifyName(), connectToHostEncrypted()
+*/
+QString QSslSocket::peerVerifyName() const
+{
+ Q_D(const QSslSocket);
+ return d->verificationPeerName;
+}
+
+/*!
+ \since 4.8
+
+ Sets a different host name, given by \a hostName, for the certificate
+ validation instead of the one used for the TCP connection.
+
+ \sa connectToHostEncrypted()
+*/
+void QSslSocket::setPeerVerifyName(const QString &hostName)
+{
+ Q_D(QSslSocket);
+ d->verificationPeerName = hostName;
+}
+
+/*!
\reimp
Returns the number of decrypted bytes that are immediately available for
@@ -791,14 +828,8 @@ void QSslSocket::setReadBufferSize(qint64 size)
Q_D(QSslSocket);
d->readBufferMaxSize = size;
- // set the plain socket's buffer size to 1k if we have a limit
- // see also the same logic in QSslSocketPrivate::createPlainSocket
- if (d->plainSocket) {
- if (d->mode == UnencryptedMode)
- d->plainSocket->setReadBufferSize(size);
- else
- d->plainSocket->setReadBufferSize(size ? 1024 : 0);
- }
+ if (d->plainSocket)
+ d->plainSocket->setReadBufferSize(size);
}
/*!
@@ -865,6 +896,7 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.peerVerifyDepth = configuration.peerVerifyDepth();
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
d->configuration.protocol = configuration.protocol();
+ d->allowRootCertOnDemandLoading = false;
}
/*!
@@ -1249,6 +1281,7 @@ void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates)
{
Q_D(QSslSocket);
d->configuration.caCertificates = certificates;
+ d->allowRootCertOnDemandLoading = false;
}
/*!
@@ -1258,6 +1291,9 @@ void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates)
handshake with addCaCertificate(), addCaCertificates(), and
setCaCertificates().
+ \note On Unix, this method may return an empty list if the root
+ certificates are loaded on demand.
+
\sa addCaCertificate(), addCaCertificates(), setCaCertificates()
*/
QList<QSslCertificate> QSslSocket::caCertificates() const
@@ -1311,10 +1347,9 @@ void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certific
/*!
Sets the default CA certificate database to \a certificates. The
default CA certificate database is originally set to your system's
- default CA certificate database. If no system default database is
- found, Qt will provide its own default database. You can override
- the default CA certificate database with your own CA certificate
- database using this function.
+ default CA certificate database. You can override the default CA
+ certificate database with your own CA certificate database using
+ this function.
Each SSL socket's CA certificate database is initialized to the
default CA certificate database.
@@ -1336,6 +1371,9 @@ void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certific
Each SSL socket's CA certificate database is initialized to the
default CA certificate database.
+ \note On Unix, this method may return an empty list if the root
+ certificates are loaded on demand.
+
\sa caCertificates()
*/
QList<QSslCertificate> QSslSocket::defaultCaCertificates()
@@ -1805,6 +1843,7 @@ QSslSocketPrivate::QSslSocketPrivate()
, connectionEncrypted(false)
, ignoreAllSslErrors(false)
, readyReadEmittedPointer(0)
+ , allowRootCertOnDemandLoading(true)
, plainSocket(0)
{
QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
@@ -1881,6 +1920,7 @@ void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciph
*/
QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates()
{
+ // ### Qt5: rename everything containing "caCertificates" to "rootCertificates" or similar
QSslSocketPrivate::ensureInitialized();
QMutexLocker locker(&globalData()->mutex);
return globalData()->config->caCertificates;
@@ -1895,6 +1935,9 @@ void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &c
QMutexLocker locker(&globalData()->mutex);
globalData()->config.detach();
globalData()->config->caCertificates = certs;
+ // when the certificates are set explicitly, we do not want to
+ // load the system certificates on demand
+ s_loadRootCertsOnDemand = false;
}
/*!
@@ -2002,6 +2045,10 @@ void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
q->setPeerName(QString());
plainSocket = new QTcpSocket(q);
+#ifndef QT_NO_BEARERMANAGEMENT
+ //copy network session down to the plain socket (if it has been set)
+ plainSocket->setProperty("_q_networksession", q->property("_q_networksession"));
+#endif
q->connect(plainSocket, SIGNAL(connected()),
q, SLOT(_q_connectedSlot()),
Qt::DirectConnection);
@@ -2194,6 +2241,20 @@ void QSslSocketPrivate::_q_flushReadBuffer()
transmit();
}
+/*!
+ \internal
+*/
+QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories()
+{
+ return QList<QByteArray>() << "/etc/ssl/certs/" // (K)ubuntu, OpenSUSE, Mandriva, MeeGo ...
+ << "/usr/lib/ssl/certs/" // Gentoo, Mandrake
+ << "/usr/share/ssl/" // Centos, Redhat, SuSE
+ << "/usr/local/ssl/" // Normal OpenSSL Tarball
+ << "/var/ssl/certs/" // AIX
+ << "/usr/local/ssl/certs/" // Solaris
+ << "/opt/openssl/certs/"; // HP-UX
+}
+
QT_END_NAMESPACE
// For private slots
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 3f3e59e..1e7c67c 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -106,6 +106,9 @@ public:
int peerVerifyDepth() const;
void setPeerVerifyDepth(int depth);
+ QString peerVerifyName() const;
+ void setPeerVerifyName(const QString &hostName);
+
// From QIODevice
qint64 bytesAvailable() const;
qint64 bytesToWrite() const;
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index b8e6c4c..8e53974 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -60,9 +60,15 @@
#include <QtCore/qvarlengtharray.h>
#include <QLibrary> // for loading the security lib for the CA store
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+// Symbian does not seem to have the symbol for SNI defined
+#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME
+#define SSL_CTRL_SET_TLSEXT_HOSTNAME 55
+#endif
+#endif
QT_BEGIN_NAMESPACE
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES)
#define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file
PtrSecCertificateGetData QSslSocketPrivate::ptrSecCertificateGetData = 0;
PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0;
@@ -80,6 +86,7 @@ QT_BEGIN_NAMESPACE
bool QSslSocketPrivate::s_libraryLoaded = false;
bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
/* \internal
@@ -252,6 +259,8 @@ init_context:
case QSsl::SslV3:
ctx = q_SSL_CTX_new(client ? q_SSLv3_client_method() : q_SSLv3_server_method());
break;
+ case QSsl::SecureProtocols: // SslV2 will be disabled below
+ case QSsl::TlsV1SslV3: // SslV2 will be disabled below
case QSsl::AnyProtocol:
default:
ctx = q_SSL_CTX_new(client ? q_SSLv23_client_method() : q_SSLv23_server_method());
@@ -277,7 +286,11 @@ init_context:
}
// Enable all bug workarounds.
- q_SSL_CTX_set_options(ctx, SSL_OP_ALL);
+ if (configuration.protocol == QSsl::TlsV1SslV3 || configuration.protocol == QSsl::SecureProtocols) {
+ q_SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
+ } else {
+ q_SSL_CTX_set_options(ctx, SSL_OP_ALL);
+ }
// Initialize ciphers
QByteArray cipherString;
@@ -326,6 +339,13 @@ init_context:
}
}
+ if (s_loadRootCertsOnDemand && allowRootCertOnDemandLoading) {
+ // tell OpenSSL the directories where to look up the root certs on demand
+ QList<QByteArray> unixDirs = unixRootCertDirectories();
+ for (int a = 0; a < unixDirs.count(); ++a)
+ q_SSL_CTX_load_verify_locations(ctx, 0, unixDirs.at(a).constData());
+ }
+
// Register a custom callback to get all verification errors.
X509_STORE_set_verify_cb_func(ctx->cert_store, q_X509Callback);
@@ -387,6 +407,29 @@ init_context:
return false;
}
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+ if ((configuration.protocol == QSsl::TlsV1SslV3 ||
+ configuration.protocol == QSsl::TlsV1 ||
+ configuration.protocol == QSsl::SecureProtocols ||
+ configuration.protocol == QSsl::AnyProtocol) &&
+ client && q_SSLeay() >= 0x00090806fL) {
+ // Set server hostname on TLS extension. RFC4366 section 3.1 requires it in ACE format.
+ QString tlsHostName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName;
+ if (tlsHostName.isEmpty())
+ tlsHostName = hostName;
+ QByteArray ace = QUrl::toAce(tlsHostName);
+ // only send the SNI header if the URL is valid and not an IP
+ if (!ace.isEmpty() && !QHostAddress().setAddress(tlsHostName)) {
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.data()))
+#else
+ if (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.constData()))
+#endif
+ qWarning("could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled");
+ }
+ }
+#endif
+
// Clear the session.
q_SSL_clear(ssl);
errorList.clear();
@@ -493,7 +536,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
resetDefaultCiphers();
//load symbols needed to receive certificates from system store
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES)
QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security");
if (securityLib.load()) {
ptrSecCertificateGetData = (PtrSecCertificateGetData) securityLib.resolve("SecCertificateGetData");
@@ -526,8 +569,22 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
} else {
qWarning("could not load crypt32 library"); // should never happen
}
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_MAC)
+ // check whether we can enable on-demand root-cert loading (i.e. check whether the sym links are there)
+ QList<QByteArray> dirs = unixRootCertDirectories();
+ QStringList symLinkFilter;
+ symLinkFilter << QLatin1String("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]");
+ for (int a = 0; a < dirs.count(); ++a) {
+ QDirIterator iterator(QLatin1String(dirs.at(a)), symLinkFilter, QDir::Files);
+ if (iterator.hasNext()) {
+ s_loadRootCertsOnDemand = true;
+ break;
+ }
+ }
#endif
- setDefaultCaCertificates(systemCaCertificates());
+ // if on-demand loading was not enabled, load the certs now
+ if (!s_loadRootCertsOnDemand)
+ setDefaultCaCertificates(systemCaCertificates());
}
/*!
@@ -760,7 +817,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
timer.start();
#endif
QList<QSslCertificate> systemCerts;
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES)
CFArrayRef cfCerts;
OSStatus status = 1;
@@ -823,15 +880,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
}
#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
QSet<QString> certFiles;
- QList<QByteArray> directories;
- directories << "/etc/ssl/certs/"; // (K)ubuntu, OpenSUSE, Mandriva, MeeGo ...
- directories << "/usr/lib/ssl/certs/"; // Gentoo, Mandrake
- directories << "/usr/share/ssl/"; // Centos, Redhat, SuSE
- directories << "/usr/local/ssl/"; // Normal OpenSSL Tarball
- directories << "/var/ssl/certs/"; // AIX
- directories << "/usr/local/ssl/certs/"; // Solaris
- directories << "/opt/openssl/certs/"; // HP-UX
-
+ QList<QByteArray> directories = unixRootCertDirectories();
QDir currentDir;
QStringList nameFilters;
nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt");
@@ -1194,19 +1243,20 @@ bool QSslSocketBackendPrivate::startHandshake()
configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
q_X509_free(x509);
+ // Start translating errors.
+ QList<QSslError> errors;
+
// check the whole chain for blacklisting (including root, as we check for subjectInfo and issuer)
foreach (const QSslCertificate &cert, configuration.peerCertificateChain) {
if (QSslCertificatePrivate::isBlacklisted(cert)) {
- q->setErrorString(QSslSocket::tr("The peer certificate is blacklisted"));
- q->setSocketError(QAbstractSocket::SslHandshakeFailedError);
- emit q->error(QAbstractSocket::SslHandshakeFailedError);
- plainSocket->disconnectFromHost();
- return false;
+ QSslError error(QSslError::CertificateBlacklisted, cert);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
}
}
- // Start translating errors.
- QList<QSslError> errors;
bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
|| (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
&& mode == QSslSocket::SslClientMode);
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index ae630bf..7e489a4 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -79,6 +79,10 @@
#include <openssl/x509_vfy.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
+#include <openssl/crypto.h>
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+#include <openssl/tls1.h>
+#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
typedef _STACK STACK;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index d370f12..90a840f 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -101,6 +101,7 @@ DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c,
DEFINEFUNC(long, ASN1_INTEGER_get, ASN1_INTEGER *a, a, return 0, return)
DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return 0, return)
DEFINEFUNC(int, ASN1_STRING_length, ASN1_STRING *a, a, return 0, return)
+DEFINEFUNC2(int, ASN1_STRING_to_UTF8, unsigned char **a, a, ASN1_STRING *b, b, return 0, return);
DEFINEFUNC4(long, BIO_ctrl, BIO *a, a, int b, b, long c, c, void *d, d, return -1, return)
DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return)
DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return)
@@ -208,6 +209,13 @@ DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return)
DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return)
DEFINEFUNC(void, SSL_load_error_strings, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(SSL *, SSL_new, SSL_CTX *a, a, return 0, return)
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, void *parg, parg, return -1, return)
+#else
+DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, const void *parg, parg, return -1, return)
+#endif
+#endif
DEFINEFUNC3(int, SSL_read, SSL *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC3(void, SSL_set_bio, SSL *a, a, BIO *b, b, BIO *c, c, return, DUMMYARG)
DEFINEFUNC(void, SSL_set_accept_state, SSL *a, a, return, DUMMYARG)
@@ -245,7 +253,10 @@ DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d
DEFINEFUNC(X509_NAME *, X509_get_issuer_name, X509 *a, a, return 0, return)
DEFINEFUNC(X509_NAME *, X509_get_subject_name, X509 *a, a, return 0, return)
DEFINEFUNC(int, X509_verify_cert, X509_STORE_CTX *a, a, return -1, return)
-DEFINEFUNC3(char *, X509_NAME_oneline, X509_NAME *a, a, char *b, b, int c, c, return 0, return)
+DEFINEFUNC(int, X509_NAME_entry_count, X509_NAME *a, a, return 0, return)
+DEFINEFUNC2(X509_NAME_ENTRY *, X509_NAME_get_entry, X509_NAME *a, a, int b, b, return 0, return)
+DEFINEFUNC(ASN1_STRING *, X509_NAME_ENTRY_get_data, X509_NAME_ENTRY *a, a, return 0, return)
+DEFINEFUNC(ASN1_OBJECT *, X509_NAME_ENTRY_get_object, X509_NAME_ENTRY *a, a, return 0, return)
DEFINEFUNC(EVP_PKEY *, X509_PUBKEY_get, X509_PUBKEY *a, a, return 0, return)
DEFINEFUNC(void, X509_STORE_free, X509_STORE *a, a, return, DUMMYARG)
DEFINEFUNC(X509_STORE *, X509_STORE_new, DUMMYARG, DUMMYARG, return 0, return)
@@ -262,6 +273,8 @@ DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c,
#endif
DEFINEFUNC(void, OPENSSL_add_all_algorithms_noconf, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(void, OPENSSL_add_all_algorithms_conf, void, DUMMYARG, return, DUMMYARG)
+DEFINEFUNC3(int, SSL_CTX_load_verify_locations, SSL_CTX *ctx, ctx, const char *CAfile, CAfile, const char *CApath, CApath, return 0, return)
+DEFINEFUNC(long, SSLeay, void, DUMMYARG, return 0, return)
#ifdef Q_OS_SYMBIAN
#define RESOLVEFUNC(func, ordinal, lib) \
@@ -513,6 +526,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(ASN1_INTEGER_get, 48, libs.second )
RESOLVEFUNC(ASN1_STRING_data, 71, libs.second )
RESOLVEFUNC(ASN1_STRING_length, 76, libs.second )
+ RESOLVEFUNC(ASN1_STRING_to_UTF8, 86, libs.second )
RESOLVEFUNC(BIO_ctrl, 184, libs.second )
RESOLVEFUNC(BIO_free, 209, libs.second )
RESOLVEFUNC(BIO_new, 222, libs.second )
@@ -585,6 +599,9 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_library_init, 137, libs.first )
RESOLVEFUNC(SSL_load_error_strings, 139, libs.first )
RESOLVEFUNC(SSL_new, 140, libs.first )
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+ RESOLVEFUNC(SSL_ctrl, 95, libs.first )
+#endif
RESOLVEFUNC(SSL_read, 143, libs.first )
RESOLVEFUNC(SSL_set_accept_state, 148, libs.first )
RESOLVEFUNC(SSL_set_bio, 149, libs.first )
@@ -599,7 +616,11 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSLv3_server_method, 197, libs.first )
RESOLVEFUNC(SSLv23_server_method, 191, libs.first )
RESOLVEFUNC(TLSv1_server_method, 200, libs.first )
- RESOLVEFUNC(X509_NAME_oneline, 1830, libs.second )
+ RESOLVEFUNC(SSL_CTX_load_verify_locations, 34, libs.first )
+ RESOLVEFUNC(X509_NAME_entry_count, 1821, libs.second )
+ RESOLVEFUNC(X509_NAME_get_entry, 1823, libs.second )
+ RESOLVEFUNC(X509_NAME_ENTRY_get_data, 1808, libs.second )
+ RESOLVEFUNC(X509_NAME_ENTRY_get_object, 1809, libs.second )
RESOLVEFUNC(X509_PUBKEY_get, 1844, libs.second )
RESOLVEFUNC(X509_STORE_free, 1939, libs.second )
RESOLVEFUNC(X509_STORE_new, 1942, libs.second )
@@ -630,6 +651,7 @@ bool q_resolveOpenSslSymbols()
#endif
RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf, 1153, libs.second )
RESOLVEFUNC(OPENSSL_add_all_algorithms_conf, 1152, libs.second )
+ RESOLVEFUNC(SSLeay, 1504, libs.second )
#else // Q_OS_SYMBIAN
#ifdef SSLEAY_MACROS
RESOLVEFUNC(ASN1_dup)
@@ -637,6 +659,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(ASN1_INTEGER_get)
RESOLVEFUNC(ASN1_STRING_data)
RESOLVEFUNC(ASN1_STRING_length)
+ RESOLVEFUNC(ASN1_STRING_to_UTF8)
RESOLVEFUNC(BIO_ctrl)
RESOLVEFUNC(BIO_free)
RESOLVEFUNC(BIO_new)
@@ -709,6 +732,9 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_library_init)
RESOLVEFUNC(SSL_load_error_strings)
RESOLVEFUNC(SSL_new)
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+ RESOLVEFUNC(SSL_ctrl)
+#endif
RESOLVEFUNC(SSL_read)
RESOLVEFUNC(SSL_set_accept_state)
RESOLVEFUNC(SSL_set_bio)
@@ -723,7 +749,10 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSLv3_server_method)
RESOLVEFUNC(SSLv23_server_method)
RESOLVEFUNC(TLSv1_server_method)
- RESOLVEFUNC(X509_NAME_oneline)
+ RESOLVEFUNC(X509_NAME_entry_count)
+ RESOLVEFUNC(X509_NAME_get_entry)
+ RESOLVEFUNC(X509_NAME_ENTRY_get_data)
+ RESOLVEFUNC(X509_NAME_ENTRY_get_object)
RESOLVEFUNC(X509_PUBKEY_get)
RESOLVEFUNC(X509_STORE_free)
RESOLVEFUNC(X509_STORE_new)
@@ -754,6 +783,8 @@ bool q_resolveOpenSslSymbols()
#endif
RESOLVEFUNC(OPENSSL_add_all_algorithms_noconf)
RESOLVEFUNC(OPENSSL_add_all_algorithms_conf)
+ RESOLVEFUNC(SSL_CTX_load_verify_locations)
+ RESOLVEFUNC(SSLeay)
#endif // Q_OS_SYMBIAN
symbolsResolved = true;
delete libs.first;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index 671f6fb..0381c4f 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -204,6 +204,7 @@ bool q_resolveOpenSslSymbols();
long q_ASN1_INTEGER_get(ASN1_INTEGER *a);
unsigned char * q_ASN1_STRING_data(ASN1_STRING *a);
int q_ASN1_STRING_length(ASN1_STRING *a);
+int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
long q_BIO_ctrl(BIO *a, int b, long c, void *d);
int q_BIO_free(BIO *a);
BIO *q_BIO_new(BIO_METHOD *a);
@@ -316,6 +317,13 @@ long q_SSL_get_verify_result(SSL *a);
int q_SSL_library_init();
void q_SSL_load_error_strings();
SSL *q_SSL_new(SSL_CTX *a);
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+long q_SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg);
+#else
+long q_SSL_ctrl(SSL *ssl,int cmd, long larg, const void *parg);
+#endif
+#endif
int q_SSL_read(SSL *a, void *b, int c);
void q_SSL_set_bio(SSL *a, BIO *b, BIO *c);
void q_SSL_set_accept_state(SSL *a);
@@ -357,7 +365,10 @@ void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d);
X509_NAME *q_X509_get_issuer_name(X509 *a);
X509_NAME *q_X509_get_subject_name(X509 *a);
int q_X509_verify_cert(X509_STORE_CTX *ctx);
-char *q_X509_NAME_oneline(X509_NAME *a, char *b, int c);
+int q_X509_NAME_entry_count(X509_NAME *a);
+X509_NAME_ENTRY *q_X509_NAME_get_entry(X509_NAME *a,int b);
+ASN1_STRING *q_X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *a);
+ASN1_OBJECT *q_X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *a);
EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a);
void q_X509_STORE_free(X509_STORE *store);
X509_STORE *q_X509_STORE_new();
@@ -412,6 +423,8 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
#endif
void q_OPENSSL_add_all_algorithms_noconf();
void q_OPENSSL_add_all_algorithms_conf();
+int q_SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+long q_SSLeay();
// Helper function
class QDateTime;
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 0f1ded8..ee6361f 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -69,10 +69,13 @@ QT_BEGIN_NAMESPACE
#if defined(Q_OS_MAC)
#include <Security/SecCertificate.h>
#include <CoreFoundation/CFArray.h>
+#ifndef QT_NO_CORESERVICES
typedef OSStatus (*PtrSecCertificateGetData)(SecCertificateRef, CSSM_DATA_PTR);
typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*);
typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*);
+#endif
#elif defined(Q_OS_WIN)
+#include <windows.h>
#include <wincrypt.h>
#ifndef HCRYPTPROV_LEGACY
#define HCRYPTPROV_LEGACY HCRYPTPROV
@@ -112,6 +115,8 @@ public:
// that was used for connecting to.
QString verificationPeerName;
+ bool allowRootCertOnDemandLoading;
+
static bool supportsSsl();
static void ensureInitialized();
static void deinitialize();
@@ -129,7 +134,7 @@ public:
static void addDefaultCaCertificate(const QSslCertificate &cert);
static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
-#if defined(Q_OS_MAC)
+#if defined(Q_OS_MAC) && !defined(QT_NO_CORESERVICES)
static PtrSecCertificateGetData ptrSecCertificateGetData;
static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates;
static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates;
@@ -168,6 +173,9 @@ private:
static bool s_libraryLoaded;
static bool s_loadedCiphersAndCerts;
+protected:
+ static bool s_loadRootCertsOnDemand;
+ static QList<QByteArray> unixRootCertDirectories();
};
QT_END_NAMESPACE