summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/network
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri70
-rw-r--r--src/network/access/qabstractnetworkcache.cpp536
-rw-r--r--src/network/access/qabstractnetworkcache.h141
-rw-r--r--src/network/access/qabstractnetworkcache_p.h66
-rw-r--r--src/network/access/qftp.cpp2437
-rw-r--r--src/network/access/qftp.h180
-rw-r--r--src/network/access/qhttp.cpp3155
-rw-r--r--src/network/access/qhttp.h315
-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.cpp1010
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h230
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp1162
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h190
-rw-r--r--src/network/access/qhttpnetworkheader.cpp134
-rw-r--r--src/network/access/qhttpnetworkheader_p.h111
-rw-r--r--src/network/access/qhttpnetworkreply.cpp951
-rw-r--r--src/network/access/qhttpnetworkreply_p.h266
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp325
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h163
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp579
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h288
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager.cpp297
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h107
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp382
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h230
-rw-r--r--src/network/access/qnetworkaccesscache.cpp379
-rw-r--r--src/network/access/qnetworkaccesscache_p.h130
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp146
-rw-r--r--src/network/access/qnetworkaccesscachebackend_p.h84
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp284
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend_p.h113
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp276
-rw-r--r--src/network/access/qnetworkaccessfilebackend_p.h97
-rw-r--r--src/network/access/qnetworkaccessftpbackend.cpp382
-rw-r--r--src/network/access/qnetworkaccessftpbackend_p.h122
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp1191
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h169
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp1299
-rw-r--r--src/network/access/qnetworkaccessmanager.h177
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h164
-rw-r--r--src/network/access/qnetworkcookie.cpp1052
-rw-r--r--src/network/access/qnetworkcookie.h124
-rw-r--r--src/network/access/qnetworkcookie_p.h100
-rw-r--r--src/network/access/qnetworkcookiejar.cpp346
-rw-r--r--src/network/access/qnetworkcookiejar.h81
-rw-r--r--src/network/access/qnetworkcookiejar_p.h74
-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.cpp728
-rw-r--r--src/network/access/qnetworkdiskcache.h97
-rw-r--r--src/network/access/qnetworkdiskcache_p.h129
-rw-r--r--src/network/access/qnetworkreply.cpp795
-rw-r--r--src/network/access/qnetworkreply.h180
-rw-r--r--src/network/access/qnetworkreply_p.h84
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp148
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h98
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp189
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h98
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp1087
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h238
-rw-r--r--src/network/access/qnetworkrequest.cpp1032
-rw-r--r--src/network/access/qnetworkrequest.h158
-rw-r--r--src/network/access/qnetworkrequest_p.h99
-rw-r--r--src/network/bearer/bearer.pri19
-rw-r--r--src/network/bearer/qbearerengine.cpp122
-rw-r--r--src/network/bearer/qbearerengine_p.h116
-rw-r--r--src/network/bearer/qbearerplugin.cpp59
-rw-r--r--src/network/bearer/qbearerplugin_p.h96
-rw-r--r--src/network/bearer/qnetworkconfigmanager.cpp357
-rw-r--r--src/network/bearer/qnetworkconfigmanager.h117
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.cpp484
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.h132
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp509
-rw-r--r--src/network/bearer/qnetworkconfiguration.h157
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h107
-rw-r--r--src/network/bearer/qnetworksession.cpp752
-rw-r--r--src/network/bearer/qnetworksession.h155
-rw-r--r--src/network/bearer/qnetworksession_p.h170
-rw-r--r--src/network/bearer/qsharednetworksession.cpp95
-rw-r--r--src/network/bearer/qsharednetworksession_p.h83
-rw-r--r--src/network/kernel/kernel.pri34
-rw-r--r--src/network/kernel/qauthenticator.cpp1428
-rw-r--r--src/network/kernel/qauthenticator.h92
-rw-r--r--src/network/kernel/qauthenticator_p.h115
-rw-r--r--src/network/kernel/qhostaddress.cpp1170
-rw-r--r--src/network/kernel/qhostaddress.h155
-rw-r--r--src/network/kernel/qhostaddress_p.h76
-rw-r--r--src/network/kernel/qhostinfo.cpp808
-rw-r--r--src/network/kernel/qhostinfo.h102
-rw-r--r--src/network/kernel/qhostinfo_p.h319
-rw-r--r--src/network/kernel/qhostinfo_symbian.cpp600
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp396
-rw-r--r--src/network/kernel/qhostinfo_win.cpp274
-rw-r--r--src/network/kernel/qnetworkinterface.cpp619
-rw-r--r--src/network/kernel/qnetworkinterface.h136
-rw-r--r--src/network/kernel/qnetworkinterface_p.h123
-rw-r--r--src/network/kernel/qnetworkinterface_symbian.cpp266
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp449
-rw-r--r--src/network/kernel/qnetworkinterface_win.cpp328
-rw-r--r--src/network/kernel/qnetworkinterface_win_p.h266
-rw-r--r--src/network/kernel/qnetworkproxy.cpp1310
-rw-r--r--src/network/kernel/qnetworkproxy.h186
-rw-r--r--src/network/kernel/qnetworkproxy_generic.cpp59
-rw-r--r--src/network/kernel/qnetworkproxy_mac.cpp242
-rw-r--r--src/network/kernel/qnetworkproxy_p.h85
-rw-r--r--src/network/kernel/qnetworkproxy_symbian.cpp267
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp443
-rw-r--r--src/network/kernel/qurlinfo.cpp731
-rw-r--r--src/network/kernel/qurlinfo.h131
-rw-r--r--src/network/network.pro31
-rw-r--r--src/network/socket/qabstractsocket.cpp2920
-rw-r--r--src/network/socket/qabstractsocket.h260
-rw-r--r--src/network/socket/qabstractsocket_p.h169
-rw-r--r--src/network/socket/qabstractsocketengine.cpp268
-rw-r--r--src/network/socket/qabstractsocketengine_p.h235
-rw-r--r--src/network/socket/qhttpsocketengine.cpp824
-rw-r--r--src/network/socket/qhttpsocketengine_p.h199
-rw-r--r--src/network/socket/qlocalserver.cpp401
-rw-r--r--src/network/socket/qlocalserver.h99
-rw-r--r--src/network/socket/qlocalserver_p.h131
-rw-r--r--src/network/socket/qlocalserver_tcp.cpp129
-rw-r--r--src/network/socket/qlocalserver_unix.cpp266
-rw-r--r--src/network/socket/qlocalserver_win.cpp210
-rw-r--r--src/network/socket/qlocalsocket.cpp507
-rw-r--r--src/network/socket/qlocalsocket.h158
-rw-r--r--src/network/socket/qlocalsocket_p.h180
-rw-r--r--src/network/socket/qlocalsocket_tcp.cpp437
-rw-r--r--src/network/socket/qlocalsocket_unix.cpp581
-rw-r--r--src/network/socket/qlocalsocket_win.cpp633
-rw-r--r--src/network/socket/qnativesocketengine.cpp1258
-rw-r--r--src/network/socket/qnativesocketengine_p.h277
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp1125
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp1439
-rw-r--r--src/network/socket/qnet_unix_p.h204
-rw-r--r--src/network/socket/qsocks5socketengine.cpp1923
-rw-r--r--src/network/socket/qsocks5socketengine_p.h299
-rw-r--r--src/network/socket/qsymbiansocketengine.cpp1730
-rw-r--r--src/network/socket/qsymbiansocketengine_p.h256
-rw-r--r--src/network/socket/qtcpserver.cpp691
-rw-r--r--src/network/socket/qtcpserver.h110
-rw-r--r--src/network/socket/qtcpsocket.cpp124
-rw-r--r--src/network/socket/qtcpsocket.h75
-rw-r--r--src/network/socket/qtcpsocket_p.h68
-rw-r--r--src/network/socket/qudpsocket.cpp567
-rw-r--r--src/network/socket/qudpsocket.h112
-rw-r--r--src/network/socket/socket.pri70
-rw-r--r--src/network/ssl/qssl.cpp123
-rw-r--r--src/network/ssl/qssl.h90
-rw-r--r--src/network/ssl/qsslcertificate.cpp858
-rw-r--r--src/network/ssl/qsslcertificate.h139
-rw-r--r--src/network/ssl/qsslcertificate_p.h108
-rw-r--r--src/network/ssl/qsslcipher.cpp238
-rw-r--r--src/network/ssl/qsslcipher.h98
-rw-r--r--src/network/ssl/qsslcipher_p.h78
-rw-r--r--src/network/ssl/qsslconfiguration.cpp542
-rw-r--r--src/network/ssl/qsslconfiguration.h137
-rw-r--r--src/network/ssl/qsslconfiguration_p.h115
-rw-r--r--src/network/ssl/qsslerror.cpp321
-rw-r--r--src/network/ssl/qsslerror.h124
-rw-r--r--src/network/ssl/qsslkey.cpp460
-rw-r--r--src/network/ssl/qsslkey.h111
-rw-r--r--src/network/ssl/qsslkey_p.h100
-rw-r--r--src/network/ssl/qsslsocket.cpp2260
-rw-r--r--src/network/ssl/qsslsocket.h227
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp1459
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h184
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp888
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h427
-rw-r--r--src/network/ssl/qsslsocket_p.h181
-rw-r--r--src/network/ssl/ssl.pri36
172 files changed, 75632 insertions, 0 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
new file mode 100644
index 0000000000..5ead3ad37f
--- /dev/null
+++ b/src/network/access/access.pri
@@ -0,0 +1,70 @@
+# Qt network access module
+
+HEADERS += \
+ access/qftp.h \
+ access/qhttp.h \
+ access/qhttpnetworkheader_p.h \
+ access/qhttpnetworkrequest_p.h \
+ access/qhttpnetworkreply_p.h \
+ access/qhttpnetworkconnection_p.h \
+ access/qhttpnetworkconnectionchannel_p.h \
+ access/qnetworkaccessauthenticationmanager_p.h \
+ access/qnetworkaccessmanager.h \
+ access/qnetworkaccessmanager_p.h \
+ access/qnetworkaccesscache_p.h \
+ access/qnetworkaccessbackend_p.h \
+ access/qnetworkaccessdebugpipebackend_p.h \
+ access/qnetworkaccesshttpbackend_p.h \
+ access/qnetworkaccessfilebackend_p.h \
+ access/qnetworkaccesscachebackend_p.h \
+ access/qnetworkaccessftpbackend_p.h \
+ access/qnetworkcookie.h \
+ 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/qhttpthreaddelegate_p.h \
+ access/qhttpmultipart.h \
+ access/qhttpmultipart_p.h
+
+SOURCES += \
+ access/qftp.cpp \
+ access/qhttp.cpp \
+ access/qhttpnetworkheader.cpp \
+ access/qhttpnetworkrequest.cpp \
+ access/qhttpnetworkreply.cpp \
+ access/qhttpnetworkconnection.cpp \
+ access/qhttpnetworkconnectionchannel.cpp \
+ access/qnetworkaccessauthenticationmanager.cpp \
+ access/qnetworkaccessmanager.cpp \
+ access/qnetworkaccesscache.cpp \
+ access/qnetworkaccessbackend.cpp \
+ access/qnetworkaccessdebugpipebackend.cpp \
+ access/qnetworkaccessfilebackend.cpp \
+ access/qnetworkaccesscachebackend.cpp \
+ access/qnetworkaccessftpbackend.cpp \
+ access/qnetworkaccesshttpbackend.cpp \
+ access/qnetworkcookie.cpp \
+ access/qnetworkcookiejar.cpp \
+ access/qnetworkrequest.cpp \
+ access/qnetworkreply.cpp \
+ access/qnetworkreplyimpl.cpp \
+ access/qnetworkreplydataimpl.cpp \
+ access/qnetworkreplyfileimpl.cpp \
+ access/qabstractnetworkcache.cpp \
+ access/qnetworkdiskcache.cpp \
+ access/qhttpthreaddelegate.cpp \
+ access/qhttpmultipart.cpp
+
+include($$PWD/../../3rdparty/zlib_dependency.pri)
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp
new file mode 100644
index 0000000000..de3fcc3286
--- /dev/null
+++ b/src/network/access/qabstractnetworkcache.cpp
@@ -0,0 +1,536 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractnetworkcache.h"
+#include "qabstractnetworkcache_p.h"
+
+#include <qdatetime.h>
+#include <qurl.h>
+
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkCacheMetaDataPrivate : public QSharedData
+{
+
+public:
+ QNetworkCacheMetaDataPrivate()
+ : QSharedData()
+ , saveToDisk(true)
+ {}
+
+ bool operator==(const QNetworkCacheMetaDataPrivate &other) const
+ {
+ return
+ url == other.url
+ && lastModified == other.lastModified
+ && expirationDate == other.expirationDate
+ && headers == other.headers
+ && saveToDisk == other.saveToDisk;
+ }
+
+ QUrl url;
+ QDateTime lastModified;
+ QDateTime expirationDate;
+ QNetworkCacheMetaData::RawHeaderList headers;
+ QNetworkCacheMetaData::AttributesMap attributes;
+ bool saveToDisk;
+
+ static void save(QDataStream &out, const QNetworkCacheMetaData &metaData);
+ static void load(QDataStream &in, QNetworkCacheMetaData &metaData);
+};
+Q_GLOBAL_STATIC(QNetworkCacheMetaDataPrivate, metadata_shared_invalid)
+
+/*!
+ \class QNetworkCacheMetaData
+ \since 4.5
+ \inmodule QtNetwork
+
+ \brief The QNetworkCacheMetaData class provides cache information.
+
+ QNetworkCacheMetaData provides information about a cache file including
+ the url, when it was last modified, when the cache file was created, headers
+ for file and if the file should be saved onto a disk.
+
+ \sa QAbstractNetworkCache
+*/
+
+/*!
+ \typedef QNetworkCacheMetaData::RawHeader
+
+ Synonym for QPair<QByteArray, QByteArray>
+*/
+
+/*!
+ \typedef QNetworkCacheMetaData::RawHeaderList
+
+ Synonym for QList<RawHeader>
+*/
+
+/*!
+ \typedef QNetworkCacheMetaData::AttributesMap
+
+ Synonym for QHash<QNetworkRequest::Attribute, QVariant>
+*/
+
+/*!
+ Constructs an invalid network cache meta data.
+
+ \sa isValid()
+ */
+QNetworkCacheMetaData::QNetworkCacheMetaData()
+ : d(new QNetworkCacheMetaDataPrivate)
+{
+}
+
+/*!
+ Destroys the network cache meta data.
+ */
+QNetworkCacheMetaData::~QNetworkCacheMetaData()
+{
+ // QSharedDataPointer takes care of freeing d
+}
+
+/*!
+ Constructs a copy of the \a other QNetworkCacheMetaData.
+ */
+QNetworkCacheMetaData::QNetworkCacheMetaData(const QNetworkCacheMetaData &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Makes a copy of the \a other QNetworkCacheMetaData and returns a reference to the copy.
+ */
+QNetworkCacheMetaData &QNetworkCacheMetaData::operator=(const QNetworkCacheMetaData &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this meta data is equal to the \a other meta data; otherwise returns false.
+
+ \sa operator!=()
+ */
+bool QNetworkCacheMetaData::operator==(const QNetworkCacheMetaData &other) const
+{
+ if (d == other.d)
+ return true;
+ if (d && other.d)
+ return *d == *other.d;
+ return false;
+}
+
+/*!
+ \fn bool QNetworkCacheMetaData::operator!=(const QNetworkCacheMetaData &other) const
+
+ Returns true if this meta data is not equal to the \a other meta data; otherwise returns false.
+
+ \sa operator==()
+ */
+
+/*!
+ Returns true if this network cache meta data has attributes that have been set otherwise false.
+ */
+bool QNetworkCacheMetaData::isValid() const
+{
+ return !(*d == *metadata_shared_invalid());
+}
+
+/*!
+ Returns is this cache should be allowed to be stored on disk.
+
+ Some cache implementations can keep these cache items in memory for performance reasons,
+ but for security reasons they should not be written to disk.
+
+ Specifically with http, documents marked with Pragma: no-cache, or have a Cache-control set to
+ no-store or no-cache or any https document that doesn't have "Cache-control: public" set will
+ set the saveToDisk to false.
+
+ \sa setSaveToDisk()
+ */
+bool QNetworkCacheMetaData::saveToDisk() const
+{
+ return d->saveToDisk;
+}
+
+/*!
+ Sets whether this network cache meta data and associated content should be
+ allowed to be stored on disk to \a allow.
+
+ \sa saveToDisk()
+ */
+void QNetworkCacheMetaData::setSaveToDisk(bool allow)
+{
+ d->saveToDisk = allow;
+}
+
+/*!
+ Returns the URL this network cache meta data is referring to.
+
+ \sa setUrl()
+ */
+QUrl QNetworkCacheMetaData::url() const
+{
+ return d->url;
+}
+
+/*!
+ Sets the URL this network cache meta data to to be \a url.
+
+ The password and fragment are removed from the url.
+
+ \sa url()
+ */
+void QNetworkCacheMetaData::setUrl(const QUrl &url)
+{
+ d->url = url;
+ d->url.setPassword(QString());
+ d->url.setFragment(QString());
+}
+
+/*!
+ Returns a list of all raw headers that are set in this meta data.
+ The list is in the same order that the headers were set.
+
+ \sa setRawHeaders()
+ */
+QNetworkCacheMetaData::RawHeaderList QNetworkCacheMetaData::rawHeaders() const
+{
+ return d->headers;
+}
+
+/*!
+ Sets the raw headers to \a list.
+
+ \sa rawHeaders()
+ */
+void QNetworkCacheMetaData::setRawHeaders(const RawHeaderList &list)
+{
+ d->headers = list;
+}
+
+/*!
+ Returns the date and time when the meta data was last modified.
+ */
+QDateTime QNetworkCacheMetaData::lastModified() const
+{
+ return d->lastModified;
+}
+
+/*!
+ Sets the date and time when the meta data was last modified to \a dateTime.
+ */
+void QNetworkCacheMetaData::setLastModified(const QDateTime &dateTime)
+{
+ d->lastModified = dateTime;
+}
+
+/*!
+ Returns the date and time when the meta data expires.
+ */
+QDateTime QNetworkCacheMetaData::expirationDate() const
+{
+ return d->expirationDate;
+}
+
+/*!
+ Sets the date and time when the meta data expires to \a dateTime.
+ */
+void QNetworkCacheMetaData::setExpirationDate(const QDateTime &dateTime)
+{
+ d->expirationDate = dateTime;
+}
+
+/*!
+ \since 4.6
+
+ Returns all the attributes stored with this cache item.
+
+ \sa setAttributes(), QNetworkRequest::Attribute
+*/
+QNetworkCacheMetaData::AttributesMap QNetworkCacheMetaData::attributes() const
+{
+ return d->attributes;
+}
+
+/*!
+ \since 4.6
+
+ Sets all attributes of this cache item to be the map \a attributes.
+
+ \sa attributes(), QNetworkRequest::setAttribute()
+*/
+void QNetworkCacheMetaData::setAttributes(const AttributesMap &attributes)
+{
+ d->attributes = attributes;
+}
+
+/*!
+ \relates QNetworkCacheMetaData
+ \since 4.5
+
+ Writes \a metaData to the \a out stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData &metaData)
+{
+ QNetworkCacheMetaDataPrivate::save(out, metaData);
+ return out;
+}
+
+static inline QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData::AttributesMap &hash)
+{
+ out << quint32(hash.size());
+ QNetworkCacheMetaData::AttributesMap::ConstIterator it = hash.end();
+ QNetworkCacheMetaData::AttributesMap::ConstIterator begin = hash.begin();
+ while (it != begin) {
+ --it;
+ out << int(it.key()) << it.value();
+ }
+ return out;
+}
+
+void QNetworkCacheMetaDataPrivate::save(QDataStream &out, const QNetworkCacheMetaData &metaData)
+{
+ // note: if you change the contents of the meta data here
+ // remember to bump the cache version in qnetworkdiskcache.cpp CurrentCacheVersion
+ out << metaData.url();
+ out << metaData.expirationDate();
+ out << metaData.lastModified();
+ out << metaData.saveToDisk();
+ out << metaData.attributes();
+ out << metaData.rawHeaders();
+}
+
+/*!
+ \relates QNetworkCacheMetaData
+ \since 4.5
+
+ Reads a QNetworkCacheMetaData from the stream \a in into \a metaData.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData &metaData)
+{
+ QNetworkCacheMetaDataPrivate::load(in, metaData);
+ return in;
+}
+
+static inline QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData::AttributesMap &hash)
+{
+ hash.clear();
+ QDataStream::Status oldStatus = in.status();
+ in.resetStatus();
+ hash.clear();
+
+ quint32 n;
+ in >> n;
+
+ for (quint32 i = 0; i < n; ++i) {
+ if (in.status() != QDataStream::Ok)
+ break;
+
+ int k;
+ QVariant t;
+ in >> k >> t;
+ hash.insertMulti(QNetworkRequest::Attribute(k), t);
+ }
+
+ if (in.status() != QDataStream::Ok)
+ hash.clear();
+ if (oldStatus != QDataStream::Ok)
+ in.setStatus(oldStatus);
+ return in;
+}
+
+void QNetworkCacheMetaDataPrivate::load(QDataStream &in, QNetworkCacheMetaData &metaData)
+{
+ in >> metaData.d->url;
+ in >> metaData.d->expirationDate;
+ in >> metaData.d->lastModified;
+ in >> metaData.d->saveToDisk;
+ in >> metaData.d->attributes;
+ in >> metaData.d->headers;
+}
+
+/*!
+ \class QAbstractNetworkCache
+ \since 4.5
+ \inmodule QtNetwork
+
+ \brief The QAbstractNetworkCache class provides the interface for cache implementations.
+
+ QAbstractNetworkCache is the base class for every standard cache that is used be
+ QNetworkAccessManager. QAbstractNetworkCache is an abstract class and cannot be
+ instantiated.
+
+ \sa QNetworkDiskCache
+*/
+
+/*!
+ Constructs an abstract network cache with the given \a parent.
+*/
+QAbstractNetworkCache::QAbstractNetworkCache(QObject *parent)
+ : QObject(*new QAbstractNetworkCachePrivate, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractNetworkCache::QAbstractNetworkCache(QAbstractNetworkCachePrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
+ Destroys the cache.
+
+ Any operations that have not been inserted are discarded.
+
+ \sa insert()
+ */
+QAbstractNetworkCache::~QAbstractNetworkCache()
+{
+}
+
+/*!
+ \fn QNetworkCacheMetaData QAbstractNetworkCache::metaData(const QUrl &url) = 0
+ Returns the meta data for the url \a url.
+
+ If the url is valid and the cache contains the data for url,
+ a valid QNetworkCacheMetaData is returned.
+
+ In the base class this is a pure virtual function.
+
+ \sa updateMetaData(), data()
+*/
+
+/*!
+ \fn void QAbstractNetworkCache::updateMetaData(const QNetworkCacheMetaData &metaData) = 0
+ Updates the cache meta date for the metaData's url to \a metaData
+
+ If the cache does not contains a cache item for the url then no action is taken.
+
+ In the base class this is a pure virtual function.
+
+ \sa metaData(), prepare()
+*/
+
+/*!
+ \fn QIODevice *QAbstractNetworkCache::data(const QUrl &url) = 0
+ Returns the data associated with \a url.
+
+ It is up to the application that requests the data to delete
+ the QIODevice when done with it.
+
+ If there is no cache for \a url, the url is invalid, or if there
+ is an internal cache error 0 is returned.
+
+ In the base class this is a pure virtual function.
+
+ \sa metaData(), prepare()
+*/
+
+/*!
+ \fn bool QAbstractNetworkCache::remove(const QUrl &url) = 0
+ Removes the cache entry for \a url, returning true if success otherwise false.
+
+ In the base class this is a pure virtual function.
+
+ \sa clear(), prepare()
+*/
+
+/*!
+ \fn QIODevice *QAbstractNetworkCache::prepare(const QNetworkCacheMetaData &metaData) = 0
+ Returns the device that should be populated with the data for
+ the cache item \a metaData. When all of the data has been written
+ insert() should be called. If metaData is invalid or the url in
+ the metadata is invalid 0 is returned.
+
+ The cache owns the device and will take care of deleting it when
+ it is inserted or removed.
+
+ To cancel a prepared inserted call remove() on the metadata's url.
+
+ In the base class this is a pure virtual function.
+
+ \sa remove(), updateMetaData(), insert()
+*/
+
+/*!
+ \fn void QAbstractNetworkCache::insert(QIODevice *device) = 0
+ Inserts the data in \a device and the prepared meta data into the cache.
+ After this function is called the data and meta data should be retrievable
+ using data() and metaData().
+
+ To cancel a prepared inserted call remove() on the metadata's url.
+
+ In the base class this is a pure virtual function.
+
+ \sa prepare(), remove()
+*/
+
+/*!
+ \fn qint64 QAbstractNetworkCache::cacheSize() const = 0
+ Returns the current size taken up by the cache. Depending upon
+ the cache implementation this might be disk or memory size.
+
+ In the base class this is a pure virtual function.
+
+ \sa clear()
+*/
+
+/*!
+ \fn void QAbstractNetworkCache::clear() = 0
+ Removes all items from the cache. Unless there was failures
+ clearing the cache cacheSize() should return 0 after a call to clear.
+
+ In the base class this is a pure virtual function.
+
+ \sa cacheSize(), remove()
+*/
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
new file mode 100644
index 0000000000..d9091d9409
--- /dev/null
+++ b/src/network/access/qabstractnetworkcache.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTNETWORKCACHE_H
+#define QABSTRACTNETWORKCACHE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qpair.h>
+#include <QtNetwork/qnetworkrequest.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QIODevice;
+class QDateTime;
+class QUrl;
+template<class T> class QList;
+
+class QNetworkCacheMetaDataPrivate;
+class Q_NETWORK_EXPORT QNetworkCacheMetaData
+{
+
+public:
+ typedef QPair<QByteArray, QByteArray> RawHeader;
+ typedef QList<RawHeader> RawHeaderList;
+ typedef QHash<QNetworkRequest::Attribute, QVariant> AttributesMap;
+
+ QNetworkCacheMetaData();
+ QNetworkCacheMetaData(const QNetworkCacheMetaData &other);
+ ~QNetworkCacheMetaData();
+
+ QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other);
+ bool operator==(const QNetworkCacheMetaData &other) const;
+ inline bool operator!=(const QNetworkCacheMetaData &other) const
+ { return !(*this == other); }
+
+ bool isValid() const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ RawHeaderList rawHeaders() const;
+ void setRawHeaders(const RawHeaderList &headers);
+
+ QDateTime lastModified() const;
+ void setLastModified(const QDateTime &dateTime);
+
+ QDateTime expirationDate() const;
+ void setExpirationDate(const QDateTime &dateTime);
+
+ bool saveToDisk() const;
+ void setSaveToDisk(bool allow);
+
+ AttributesMap attributes() const;
+ void setAttributes(const AttributesMap &attributes);
+
+private:
+ friend class QNetworkCacheMetaDataPrivate;
+ QSharedDataPointer<QNetworkCacheMetaDataPrivate> d;
+};
+
+Q_NETWORK_EXPORT QDataStream &operator<<(QDataStream &, const QNetworkCacheMetaData &);
+Q_NETWORK_EXPORT QDataStream &operator>>(QDataStream &, QNetworkCacheMetaData &);
+
+
+class QAbstractNetworkCachePrivate;
+class Q_NETWORK_EXPORT QAbstractNetworkCache : public QObject
+{
+ Q_OBJECT
+
+public:
+ virtual ~QAbstractNetworkCache();
+
+ virtual QNetworkCacheMetaData metaData(const QUrl &url) = 0;
+ virtual void updateMetaData(const QNetworkCacheMetaData &metaData) = 0;
+ virtual QIODevice *data(const QUrl &url) = 0;
+ virtual bool remove(const QUrl &url) = 0;
+ virtual qint64 cacheSize() const = 0;
+
+ virtual QIODevice *prepare(const QNetworkCacheMetaData &metaData) = 0;
+ virtual void insert(QIODevice *device) = 0;
+
+public Q_SLOTS:
+ virtual void clear() = 0;
+
+protected:
+ explicit QAbstractNetworkCache(QObject *parent = 0);
+ QAbstractNetworkCache(QAbstractNetworkCachePrivate &dd, QObject *parent);
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractNetworkCache)
+ Q_DISABLE_COPY(QAbstractNetworkCache)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qabstractnetworkcache_p.h b/src/network/access/qabstractnetworkcache_p.h
new file mode 100644
index 0000000000..aba95213b7
--- /dev/null
+++ b/src/network/access/qabstractnetworkcache_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTNETWORKCACHE_P_H
+#define QABSTRACTNETWORKCACHE_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.
+//
+
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractNetworkCachePrivate: public QObjectPrivate
+{
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
new file mode 100644
index 0000000000..4ff45babaa
--- /dev/null
+++ b/src/network/access/qftp.cpp
@@ -0,0 +1,2437 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QFTPPI_DEBUG
+//#define QFTPDTP_DEBUG
+
+#include "qftp.h"
+#include "qabstractsocket.h"
+
+#ifndef QT_NO_FTP
+
+#include "qcoreapplication.h"
+#include "qtcpsocket.h"
+#include "qurlinfo.h"
+#include "qstringlist.h"
+#include "qregexp.h"
+#include "qtimer.h"
+#include "qfileinfo.h"
+#include "qhash.h"
+#include "qtcpserver.h"
+#include "qlocale.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFtpPI;
+
+/*
+ The QFtpDTP (DTP = Data Transfer Process) controls all client side
+ data transfer between the client and server.
+*/
+class QFtpDTP : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ConnectState {
+ CsHostFound,
+ CsConnected,
+ CsClosed,
+ CsHostNotFound,
+ CsConnectionRefused
+ };
+
+ QFtpDTP(QFtpPI *p, QObject *parent = 0);
+
+ void setData(QByteArray *);
+ void setDevice(QIODevice *);
+ void writeData();
+ void setBytesTotal(qint64 bytes);
+
+ bool hasError() const;
+ QString errorMessage() const;
+ void clearError();
+
+ void connectToHost(const QString & host, quint16 port);
+ int setupListener(const QHostAddress &address);
+ void waitForConnection();
+
+ QTcpSocket::SocketState state() const;
+ qint64 bytesAvailable() const;
+ qint64 read(char *data, qint64 maxlen);
+ QByteArray readAll();
+
+ void abortConnection();
+
+ static bool parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info);
+
+signals:
+ void listInfo(const QUrlInfo&);
+ void readyRead();
+ void dataTransferProgress(qint64, qint64);
+
+ void connectState(int);
+
+private slots:
+ void socketConnected();
+ void socketReadyRead();
+ void socketError(QAbstractSocket::SocketError);
+ void socketConnectionClosed();
+ void socketBytesWritten(qint64);
+ void setupSocket();
+
+ void dataReadyRead();
+
+private:
+ void clearData();
+
+ QTcpSocket *socket;
+ QTcpServer listener;
+
+ QFtpPI *pi;
+ QString err;
+ qint64 bytesDone;
+ qint64 bytesTotal;
+ bool callWriteData;
+
+ // If is_ba is true, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+
+ QByteArray bytesFromSocket;
+};
+
+/**********************************************************************
+ *
+ * QFtpPI - Protocol Interpreter
+ *
+ *********************************************************************/
+
+class QFtpPI : public QObject
+{
+ Q_OBJECT
+
+public:
+ QFtpPI(QObject *parent = 0);
+
+ void connectToHost(const QString &host, quint16 port);
+
+ bool sendCommands(const QStringList &cmds);
+ bool sendCommand(const QString &cmd)
+ { return sendCommands(QStringList(cmd)); }
+
+ void clearPendingCommands();
+ void abort();
+
+ QString currentCommand() const
+ { return currentCmd; }
+
+ bool rawCommand;
+ bool transferConnectionExtended;
+
+ QFtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it
+ // makes the design simpler this way
+signals:
+ void connectState(int);
+ void finished(const QString&);
+ void error(int, const QString&);
+ void rawFtpReply(int, const QString&);
+
+private slots:
+ void hostFound();
+ void connected();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void error(QAbstractSocket::SocketError);
+
+ void dtpConnectState(int);
+
+private:
+ // the states are modelled after the generalized state diagram of RFC 959,
+ // page 58
+ enum State {
+ Begin,
+ Idle,
+ Waiting,
+ Success,
+ Failure
+ };
+
+ enum AbortState {
+ None,
+ AbortStarted,
+ WaitForAbortToFinish
+ };
+
+ bool processReply();
+ bool startNextCmd();
+
+ QTcpSocket commandSocket;
+ QString replyText;
+ char replyCode[3];
+ State state;
+ AbortState abortState;
+ QStringList pendingCommands;
+ QString currentCmd;
+
+ bool waitForDtpToConnect;
+ bool waitForDtpToClose;
+
+ QByteArray bytesFromSocket;
+
+ friend class QFtpDTP;
+};
+
+/**********************************************************************
+ *
+ * QFtpCommand implemenatation
+ *
+ *********************************************************************/
+class QFtpCommand
+{
+public:
+ QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba);
+ QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev = 0);
+ ~QFtpCommand();
+
+ int id;
+ QFtp::Command command;
+ QStringList rawCmds;
+
+ // If is_ba is true, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+
+ static QBasicAtomicInt idCounter;
+};
+
+QBasicAtomicInt QFtpCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba)
+ : command(cmd), rawCmds(raw), is_ba(true)
+{
+ id = idCounter.fetchAndAddRelaxed(1);
+ data.ba = new QByteArray(ba);
+}
+
+QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev)
+ : command(cmd), rawCmds(raw), is_ba(false)
+{
+ id = idCounter.fetchAndAddRelaxed(1);
+ data.dev = dev;
+}
+
+QFtpCommand::~QFtpCommand()
+{
+ if (is_ba)
+ delete data.ba;
+}
+
+/**********************************************************************
+ *
+ * QFtpDTP implemenatation
+ *
+ *********************************************************************/
+QFtpDTP::QFtpDTP(QFtpPI *p, QObject *parent) :
+ QObject(parent),
+ socket(0),
+ listener(this),
+ pi(p),
+ callWriteData(false)
+{
+ clearData();
+ listener.setObjectName(QLatin1String("QFtpDTP active state server"));
+ connect(&listener, SIGNAL(newConnection()), SLOT(setupSocket()));
+}
+
+void QFtpDTP::setData(QByteArray *ba)
+{
+ is_ba = true;
+ data.ba = ba;
+}
+
+void QFtpDTP::setDevice(QIODevice *dev)
+{
+ is_ba = false;
+ data.dev = dev;
+}
+
+void QFtpDTP::setBytesTotal(qint64 bytes)
+{
+ bytesTotal = bytes;
+ bytesDone = 0;
+ emit dataTransferProgress(bytesDone, bytesTotal);
+}
+
+void QFtpDTP::connectToHost(const QString & host, quint16 port)
+{
+ bytesFromSocket.clear();
+
+ if (socket) {
+ delete socket;
+ 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()));
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
+ connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));
+ connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
+
+ socket->connectToHost(host, 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();
+}
+
+void QFtpDTP::waitForConnection()
+{
+ // This function is only interesting in Active transfer mode; it works
+ // around a limitation in QFtp's design by blocking, waiting for an
+ // incoming connection. For the default Passive mode, it does nothing.
+ if (listener.isListening())
+ listener.waitForNewConnection();
+}
+
+QTcpSocket::SocketState QFtpDTP::state() const
+{
+ return socket ? socket->state() : QTcpSocket::UnconnectedState;
+}
+
+qint64 QFtpDTP::bytesAvailable() const
+{
+ if (!socket || socket->state() != QTcpSocket::ConnectedState)
+ return (qint64) bytesFromSocket.size();
+ return socket->bytesAvailable();
+}
+
+qint64 QFtpDTP::read(char *data, qint64 maxlen)
+{
+ qint64 read;
+ if (socket && socket->state() == QTcpSocket::ConnectedState) {
+ read = socket->read(data, maxlen);
+ } else {
+ read = qMin(maxlen, qint64(bytesFromSocket.size()));
+ memcpy(data, bytesFromSocket.data(), read);
+ bytesFromSocket.remove(0, read);
+ }
+
+ bytesDone += read;
+ return read;
+}
+
+QByteArray QFtpDTP::readAll()
+{
+ QByteArray tmp;
+ if (socket && socket->state() == QTcpSocket::ConnectedState) {
+ tmp = socket->readAll();
+ bytesDone += tmp.size();
+ } else {
+ tmp = bytesFromSocket;
+ bytesFromSocket.clear();
+ }
+ return tmp;
+}
+
+void QFtpDTP::writeData()
+{
+ if (!socket)
+ return;
+
+ if (is_ba) {
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::writeData: write %d bytes", data.ba->size());
+#endif
+ if (data.ba->size() == 0)
+ emit dataTransferProgress(0, bytesTotal);
+ else
+ socket->write(data.ba->data(), data.ba->size());
+
+ socket->close();
+
+ clearData();
+ } else if (data.dev) {
+ callWriteData = false;
+ const qint64 blockSize = 16*1024;
+ char buf[16*1024];
+ qint64 read = data.dev->read(buf, blockSize);
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::writeData: write() of size %lli bytes", read);
+#endif
+ if (read > 0) {
+ socket->write(buf, read);
+ } else if (read == -1 || (!data.dev->isSequential() && data.dev->atEnd())) {
+ // error or EOF
+ if (bytesDone == 0 && socket->bytesToWrite() == 0)
+ emit dataTransferProgress(0, bytesTotal);
+ socket->close();
+ clearData();
+ }
+
+ // do we continue uploading?
+ callWriteData = data.dev != 0;
+ }
+}
+
+void QFtpDTP::dataReadyRead()
+{
+ writeData();
+}
+
+inline bool QFtpDTP::hasError() const
+{
+ return !err.isNull();
+}
+
+inline QString QFtpDTP::errorMessage() const
+{
+ return err;
+}
+
+inline void QFtpDTP::clearError()
+{
+ err.clear();
+}
+
+void QFtpDTP::abortConnection()
+{
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::abortConnection, bytesAvailable == %lli",
+ socket ? socket->bytesAvailable() : (qint64) 0);
+#endif
+ callWriteData = false;
+ clearData();
+
+ if (socket)
+ socket->abort();
+}
+
+static void _q_fixupDateTime(QDateTime *dateTime)
+{
+ // Adjust for future tolerance.
+ const int futureTolerance = 86400;
+ if (dateTime->secsTo(QDateTime::currentDateTime()) < -futureTolerance) {
+ QDate d = dateTime->date();
+ d.setYMD(d.year() - 1, d.month(), d.day());
+ dateTime->setDate(d);
+ }
+}
+
+static void _q_parseUnixDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
+{
+ // Unix style, 7 + 1 entries
+ // -rw-r--r-- 1 ftp ftp 17358091 Aug 10 2004 qt-x11-free-3.3.3.tar.gz
+ // drwxr-xr-x 3 ftp ftp 4096 Apr 14 2000 compiled-examples
+ // lrwxrwxrwx 1 ftp ftp 9 Oct 29 2005 qtscape -> qtmozilla
+ if (tokens.size() != 8)
+ return;
+
+ char first = tokens.at(1).at(0).toLatin1();
+ if (first == 'd') {
+ info->setDir(true);
+ info->setFile(false);
+ info->setSymLink(false);
+ } else if (first == '-') {
+ info->setDir(false);
+ info->setFile(true);
+ info->setSymLink(false);
+ } else if (first == 'l') {
+ info->setDir(true);
+ info->setFile(false);
+ info->setSymLink(true);
+ }
+
+ // Resolve filename
+ QString name = tokens.at(7);
+ if (info->isSymLink()) {
+ int linkPos = name.indexOf(QLatin1String(" ->"));
+ if (linkPos != -1)
+ name.resize(linkPos);
+ }
+ info->setName(name);
+
+ // Resolve owner & group
+ info->setOwner(tokens.at(3));
+ info->setGroup(tokens.at(4));
+
+ // Resolve size
+ info->setSize(tokens.at(5).toLongLong());
+
+ QStringList formats;
+ formats << QLatin1String("MMM dd yyyy") << QLatin1String("MMM dd hh:mm") << QLatin1String("MMM d yyyy")
+ << QLatin1String("MMM d hh:mm") << QLatin1String("MMM d yyyy") << QLatin1String("MMM dd yyyy");
+
+ QString dateString = tokens.at(6);
+ dateString[0] = dateString[0].toUpper();
+
+ // Resolve the modification date by parsing all possible formats
+ QDateTime dateTime;
+ int n = 0;
+#ifndef QT_NO_DATESTRING
+ do {
+ dateTime = QLocale::c().toDateTime(dateString, formats.at(n++));
+ } while (n < formats.size() && (!dateTime.isValid()));
+#endif
+
+ if (n == 2 || n == 4) {
+ // Guess the year.
+ dateTime.setDate(QDate(QDate::currentDate().year(),
+ dateTime.date().month(),
+ dateTime.date().day()));
+ _q_fixupDateTime(&dateTime);
+ }
+ if (dateTime.isValid())
+ info->setLastModified(dateTime);
+
+ // Resolve permissions
+ int permissions = 0;
+ QString p = tokens.at(2);
+ permissions |= (p[0] == QLatin1Char('r') ? QUrlInfo::ReadOwner : 0);
+ permissions |= (p[1] == QLatin1Char('w') ? QUrlInfo::WriteOwner : 0);
+ permissions |= (p[2] == QLatin1Char('x') ? QUrlInfo::ExeOwner : 0);
+ permissions |= (p[3] == QLatin1Char('r') ? QUrlInfo::ReadGroup : 0);
+ permissions |= (p[4] == QLatin1Char('w') ? QUrlInfo::WriteGroup : 0);
+ permissions |= (p[5] == QLatin1Char('x') ? QUrlInfo::ExeGroup : 0);
+ permissions |= (p[6] == QLatin1Char('r') ? QUrlInfo::ReadOther : 0);
+ permissions |= (p[7] == QLatin1Char('w') ? QUrlInfo::WriteOther : 0);
+ permissions |= (p[8] == QLatin1Char('x') ? QUrlInfo::ExeOther : 0);
+ info->setPermissions(permissions);
+
+ bool isOwner = info->owner() == userName;
+ info->setReadable((permissions & QUrlInfo::ReadOther) || ((permissions & QUrlInfo::ReadOwner) && isOwner));
+ info->setWritable((permissions & QUrlInfo::WriteOther) || ((permissions & QUrlInfo::WriteOwner) && isOwner));
+}
+
+static void _q_parseDosDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
+{
+ // DOS style, 3 + 1 entries
+ // 01-16-02 11:14AM <DIR> epsgroup
+ // 06-05-03 03:19PM 1973 readme.txt
+ if (tokens.size() != 4)
+ return;
+
+ Q_UNUSED(userName);
+
+ QString name = tokens.at(3);
+ info->setName(name);
+ info->setSymLink(name.toLower().endsWith(QLatin1String(".lnk")));
+
+ if (tokens.at(2) == QLatin1String("<DIR>")) {
+ info->setFile(false);
+ info->setDir(true);
+ } else {
+ info->setFile(true);
+ info->setDir(false);
+ info->setSize(tokens.at(2).toLongLong());
+ }
+
+ // Note: We cannot use QFileInfo; permissions are for the server-side
+ // machine, and QFileInfo's behavior depends on the local platform.
+ int permissions = QUrlInfo::ReadOwner | QUrlInfo::WriteOwner
+ | QUrlInfo::ReadGroup | QUrlInfo::WriteGroup
+ | QUrlInfo::ReadOther | QUrlInfo::WriteOther;
+ QString ext;
+ int extIndex = name.lastIndexOf(QLatin1Char('.'));
+ if (extIndex != -1)
+ ext = name.mid(extIndex + 1);
+ if (ext == QLatin1String("exe") || ext == QLatin1String("bat") || ext == QLatin1String("com"))
+ permissions |= QUrlInfo::ExeOwner | QUrlInfo::ExeGroup | QUrlInfo::ExeOther;
+ info->setPermissions(permissions);
+
+ info->setReadable(true);
+ info->setWritable(info->isFile());
+
+ QDateTime dateTime;
+#ifndef QT_NO_DATESTRING
+ dateTime = QLocale::c().toDateTime(tokens.at(1), QLatin1String("MM-dd-yy hh:mmAP"));
+ if (dateTime.date().year() < 1971) {
+ dateTime.setDate(QDate(dateTime.date().year() + 100,
+ dateTime.date().month(),
+ dateTime.date().day()));
+ }
+#endif
+
+ info->setLastModified(dateTime);
+
+}
+
+bool QFtpDTP::parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info)
+{
+ if (buffer.isEmpty())
+ return false;
+
+ QString bufferStr = QString::fromLatin1(buffer).trimmed();
+
+ // Unix style FTP servers
+ QRegExp unixPattern(QLatin1String("^([\\-dl])([a-zA-Z\\-]{9,9})\\s+\\d+\\s+(\\S*)\\s+"
+ "(\\S*)\\s+(\\d+)\\s+(\\S+\\s+\\S+\\s+\\S+)\\s+(\\S.*)"));
+ if (unixPattern.indexIn(bufferStr) == 0) {
+ _q_parseUnixDir(unixPattern.capturedTexts(), userName, info);
+ return true;
+ }
+
+ // DOS style FTP servers
+ QRegExp dosPattern(QLatin1String("^(\\d\\d-\\d\\d-\\d\\d\\ \\ \\d\\d:\\d\\d[AP]M)\\s+"
+ "(<DIR>|\\d+)\\s+(\\S.*)$"));
+ if (dosPattern.indexIn(bufferStr) == 0) {
+ _q_parseDosDir(dosPattern.capturedTexts(), userName, info);
+ return true;
+ }
+
+ // Unsupported
+ return false;
+}
+
+void QFtpDTP::socketConnected()
+{
+ bytesDone = 0;
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::connectState(CsConnected)");
+#endif
+ emit connectState(QFtpDTP::CsConnected);
+}
+
+void QFtpDTP::socketReadyRead()
+{
+ if (!socket)
+ return;
+
+ if (pi->currentCommand().isEmpty()) {
+ socket->close();
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::connectState(CsClosed)");
+#endif
+ emit connectState(QFtpDTP::CsClosed);
+ return;
+ }
+
+ if (pi->abortState == QFtpPI::AbortStarted) {
+ // discard data
+ socket->readAll();
+ return;
+ }
+
+ if (pi->currentCommand().startsWith(QLatin1String("LIST"))) {
+ while (socket->canReadLine()) {
+ QUrlInfo i;
+ QByteArray line = socket->readLine();
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP read (list): '%s'", line.constData());
+#endif
+ if (parseDir(line, QLatin1String(""), &i)) {
+ emit listInfo(i);
+ } else {
+ // some FTP servers don't return a 550 if the file or directory
+ // does not exist, but rather write a text to the data socket
+ // -- try to catch these cases
+ if (line.endsWith("No such file or directory\r\n"))
+ err = QString::fromLatin1(line);
+ }
+ }
+ } else {
+ if (!is_ba && data.dev) {
+ do {
+ QByteArray ba;
+ ba.resize(socket->bytesAvailable());
+ qint64 bytesRead = socket->read(ba.data(), ba.size());
+ if (bytesRead < 0) {
+ // a read following a readyRead() signal will
+ // never fail.
+ return;
+ }
+ ba.resize(bytesRead);
+ bytesDone += bytesRead;
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP read: %lli bytes (total %lli bytes)", bytesRead, bytesDone);
+#endif
+ if (data.dev) // make sure it wasn't deleted in the slot
+ data.dev->write(ba);
+ emit dataTransferProgress(bytesDone, bytesTotal);
+
+ // Need to loop; dataTransferProgress is often connected to
+ // slots that update the GUI (e.g., progress bar values), and
+ // if events are processed, more data may have arrived.
+ } while (socket->bytesAvailable());
+ } else {
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP readyRead: %lli bytes available (total %lli bytes read)",
+ bytesAvailable(), bytesDone);
+#endif
+ emit dataTransferProgress(bytesDone+socket->bytesAvailable(), bytesTotal);
+ emit readyRead();
+ }
+ }
+}
+
+void QFtpDTP::socketError(QAbstractSocket::SocketError e)
+{
+ if (e == QTcpSocket::HostNotFoundError) {
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::connectState(CsHostNotFound)");
+#endif
+ emit connectState(QFtpDTP::CsHostNotFound);
+ } else if (e == QTcpSocket::ConnectionRefusedError) {
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::connectState(CsConnectionRefused)");
+#endif
+ emit connectState(QFtpDTP::CsConnectionRefused);
+ }
+}
+
+void QFtpDTP::socketConnectionClosed()
+{
+ if (!is_ba && data.dev) {
+ clearData();
+ }
+
+ bytesFromSocket = socket->readAll();
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::connectState(CsClosed)");
+#endif
+ emit connectState(QFtpDTP::CsClosed);
+}
+
+void QFtpDTP::socketBytesWritten(qint64 bytes)
+{
+ bytesDone += bytes;
+#if defined(QFTPDTP_DEBUG)
+ qDebug("QFtpDTP::bytesWritten(%lli)", bytesDone);
+#endif
+ emit dataTransferProgress(bytesDone, bytesTotal);
+ if (callWriteData)
+ writeData();
+}
+
+void QFtpDTP::setupSocket()
+{
+ socket = listener.nextPendingConnection();
+ socket->setObjectName(QLatin1String("QFtpDTP Active state socket"));
+ connect(socket, SIGNAL(connected()), SLOT(socketConnected()));
+ connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
+ connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed()));
+ connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
+
+ listener.close();
+}
+
+void QFtpDTP::clearData()
+{
+ is_ba = false;
+ data.dev = 0;
+}
+
+/**********************************************************************
+ *
+ * QFtpPI implemenatation
+ *
+ *********************************************************************/
+QFtpPI::QFtpPI(QObject *parent) :
+ QObject(parent),
+ rawCommand(false),
+ transferConnectionExtended(true),
+ dtp(this),
+ commandSocket(0),
+ state(Begin), abortState(None),
+ currentCmd(QString()),
+ waitForDtpToConnect(false),
+ waitForDtpToClose(false)
+{
+ commandSocket.setObjectName(QLatin1String("QFtpPI_socket"));
+ connect(&commandSocket, SIGNAL(hostFound()),
+ SLOT(hostFound()));
+ connect(&commandSocket, SIGNAL(connected()),
+ SLOT(connected()));
+ connect(&commandSocket, SIGNAL(disconnected()),
+ SLOT(connectionClosed()));
+ connect(&commandSocket, SIGNAL(readyRead()),
+ SLOT(readyRead()));
+ connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ SLOT(error(QAbstractSocket::SocketError)));
+
+ connect(&dtp, SIGNAL(connectState(int)),
+ SLOT(dtpConnectState(int)));
+}
+
+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);
+}
+
+/*
+ Sends the sequence of commands \a cmds to the FTP server. When the commands
+ are all done the finished() signal is emitted. When an error occurs, the
+ error() signal is emitted.
+
+ If there are pending commands in the queue this functions returns false and
+ the \a cmds are not added to the queue; otherwise it returns true.
+*/
+bool QFtpPI::sendCommands(const QStringList &cmds)
+{
+ if (!pendingCommands.isEmpty())
+ return false;
+
+ if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {
+ emit error(QFtp::NotConnected, QFtp::tr("Not connected"));
+ return true; // there are no pending commands
+ }
+
+ pendingCommands = cmds;
+ startNextCmd();
+ return true;
+}
+
+void QFtpPI::clearPendingCommands()
+{
+ pendingCommands.clear();
+ dtp.abortConnection();
+ currentCmd.clear();
+ state = Idle;
+}
+
+void QFtpPI::abort()
+{
+ pendingCommands.clear();
+
+ if (abortState != None)
+ // ABOR already sent
+ return;
+
+ abortState = AbortStarted;
+#if defined(QFTPPI_DEBUG)
+ qDebug("QFtpPI send: ABOR");
+#endif
+ commandSocket.write("ABOR\r\n", 6);
+
+ if (currentCmd.startsWith(QLatin1String("STOR ")))
+ dtp.abortConnection();
+}
+
+void QFtpPI::hostFound()
+{
+ emit connectState(QFtp::Connecting);
+}
+
+void QFtpPI::connected()
+{
+ state = Begin;
+#if defined(QFTPPI_DEBUG)
+// qDebug("QFtpPI state: %d [connected()]", state);
+#endif
+ // try to improve performance by setting TCP_NODELAY
+ commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
+
+ emit connectState(QFtp::Connected);
+}
+
+void QFtpPI::connectionClosed()
+{
+ commandSocket.close();
+ emit connectState(QFtp::Unconnected);
+}
+
+void QFtpPI::delayedCloseFinished()
+{
+ emit connectState(QFtp::Unconnected);
+}
+
+void QFtpPI::error(QAbstractSocket::SocketError e)
+{
+ if (e == QTcpSocket::HostNotFoundError) {
+ emit connectState(QFtp::Unconnected);
+ emit error(QFtp::HostNotFound,
+ QFtp::tr("Host %1 not found").arg(commandSocket.peerName()));
+ } else if (e == QTcpSocket::ConnectionRefusedError) {
+ emit connectState(QFtp::Unconnected);
+ emit error(QFtp::ConnectionRefused,
+ QFtp::tr("Connection refused to host %1").arg(commandSocket.peerName()));
+ } else if (e == QTcpSocket::SocketTimeoutError) {
+ emit connectState(QFtp::Unconnected);
+ emit error(QFtp::ConnectionRefused,
+ QFtp::tr("Connection timed out to host %1").arg(commandSocket.peerName()));
+ }
+}
+
+void QFtpPI::readyRead()
+{
+ if (waitForDtpToClose)
+ return;
+
+ while (commandSocket.canReadLine()) {
+ // read line with respect to line continuation
+ QString line = QString::fromAscii(commandSocket.readLine());
+ if (replyText.isEmpty()) {
+ if (line.length() < 3) {
+ // protocol error
+ return;
+ }
+ const int lowerLimit[3] = {1,0,0};
+ const int upperLimit[3] = {5,5,9};
+ for (int i=0; i<3; i++) {
+ replyCode[i] = line[i].digitValue();
+ if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {
+ // protocol error
+ return;
+ }
+ }
+ }
+ QString endOfMultiLine;
+ endOfMultiLine[0] = '0' + replyCode[0];
+ endOfMultiLine[1] = '0' + replyCode[1];
+ endOfMultiLine[2] = '0' + replyCode[2];
+ endOfMultiLine[3] = QLatin1Char(' ');
+ QString lineCont(endOfMultiLine);
+ lineCont[3] = QLatin1Char('-');
+ QString lineLeft4 = line.left(4);
+
+ while (lineLeft4 != endOfMultiLine) {
+ if (lineLeft4 == lineCont)
+ replyText += line.mid(4); // strip 'xyz-'
+ else
+ replyText += line;
+ if (!commandSocket.canReadLine())
+ return;
+ line = QString::fromAscii(commandSocket.readLine());
+ lineLeft4 = line.left(4);
+ }
+ replyText += line.mid(4); // strip reply code 'xyz '
+ if (replyText.endsWith(QLatin1String("\r\n")))
+ replyText.chop(2);
+
+ if (processReply())
+ replyText = QLatin1String("");
+ }
+}
+
+/*
+ Process a reply from the FTP server.
+
+ Returns true if the reply was processed or false if the reply has to be
+ processed at a later point.
+*/
+bool QFtpPI::processReply()
+{
+#if defined(QFTPPI_DEBUG)
+// qDebug("QFtpPI state: %d [processReply() begin]", state);
+ if (replyText.length() < 400)
+ qDebug("QFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());
+ else
+ qDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]);
+#endif
+
+ int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
+
+ // process 226 replies ("Closing Data Connection") only when the data
+ // connection is really closed to avoid short reads of the DTP
+ if (replyCodeInt == 226 || (replyCodeInt == 250 && currentCmd.startsWith(QLatin1String("RETR")))) {
+ if (dtp.state() != QTcpSocket::UnconnectedState) {
+ waitForDtpToClose = true;
+ return false;
+ }
+ }
+
+ switch (abortState) {
+ case AbortStarted:
+ abortState = WaitForAbortToFinish;
+ break;
+ case WaitForAbortToFinish:
+ abortState = None;
+ return true;
+ default:
+ break;
+ }
+
+ // get new state
+ static const State table[5] = {
+ /* 1yz 2yz 3yz 4yz 5yz */
+ Waiting, Success, Idle, Failure, Failure
+ };
+ switch (state) {
+ case Begin:
+ if (replyCode[0] == 1) {
+ return true;
+ } else if (replyCode[0] == 2) {
+ state = Idle;
+ emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));
+ break;
+ }
+ // reply codes not starting with 1 or 2 are not handled.
+ return true;
+ case Waiting:
+ if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)
+ state = Failure;
+ else
+#if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
+ {
+ // work around a crash on 64 bit gcc IRIX
+ State *t = (State *) table;
+ state = t[replyCode[0] - 1];
+ }
+#else
+ if (replyCodeInt == 202)
+ state = Failure;
+ else
+ state = table[replyCode[0] - 1];
+#endif
+ break;
+ default:
+ // ignore unrequested message
+ return true;
+ }
+#if defined(QFTPPI_DEBUG)
+// qDebug("QFtpPI state: %d [processReply() intermediate]", state);
+#endif
+
+ // special actions on certain replies
+ emit rawFtpReply(replyCodeInt, replyText);
+ if (rawCommand) {
+ rawCommand = false;
+ } else if (replyCodeInt == 227) {
+ // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
+ // rfc959 does not define this response precisely, and gives
+ // both examples where the parenthesis are used, and where
+ // they are missing. We need to scan for the address and host
+ // info.
+ QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
+ if (addrPortPattern.indexIn(replyText) == -1) {
+#if defined(QFTPPI_DEBUG)
+ qDebug("QFtp: bad 227 response -- address and port information missing");
+#endif
+ // this error should be reported
+ } else {
+ QStringList lst = addrPortPattern.capturedTexts();
+ QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];
+ quint16 port = (lst[5].toUInt() << 8) + lst[6].toUInt();
+ waitForDtpToConnect = true;
+ dtp.connectToHost(host, port);
+ }
+ } else if (replyCodeInt == 229) {
+ // 229 Extended Passive mode OK (|||10982|)
+ int portPos = replyText.indexOf(QLatin1Char('('));
+ if (portPos == -1) {
+#if defined(QFTPPI_DEBUG)
+ qDebug("QFtp: bad 229 response -- port information missing");
+#endif
+ // this error should be reported
+ } else {
+ ++portPos;
+ QChar delimiter = replyText.at(portPos);
+ QStringList epsvParameters = replyText.mid(portPos).split(delimiter);
+
+ waitForDtpToConnect = true;
+ dtp.connectToHost(commandSocket.peerAddress().toString(),
+ epsvParameters.at(3).toInt());
+ }
+
+ } else if (replyCodeInt == 230) {
+ if (currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
+ pendingCommands.first().startsWith(QLatin1String("PASS "))) {
+ // no need to send the PASS -- we are already logged in
+ pendingCommands.pop_front();
+ }
+ // 230 User logged in, proceed.
+ emit connectState(QFtp::LoggedIn);
+ } else if (replyCodeInt == 213) {
+ // 213 File status.
+ if (currentCmd.startsWith(QLatin1String("SIZE ")))
+ dtp.setBytesTotal(replyText.simplified().toLongLong());
+ } else if (replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR "))) {
+ dtp.waitForConnection();
+ dtp.writeData();
+ }
+
+ // react on new state
+ switch (state) {
+ case Begin:
+ // should never happen
+ break;
+ case Success:
+ // success handling
+ state = Idle;
+ // no break!
+ case Idle:
+ if (dtp.hasError()) {
+ emit error(QFtp::UnknownError, dtp.errorMessage());
+ dtp.clearError();
+ }
+ startNextCmd();
+ break;
+ case Waiting:
+ // do nothing
+ break;
+ case Failure:
+ // If the EPSV or EPRT commands fail, replace them with
+ // the old PASV and PORT instead and try again.
+ if (currentCmd.startsWith(QLatin1String("EPSV"))) {
+ transferConnectionExtended = false;
+ pendingCommands.prepend(QLatin1String("PASV\r\n"));
+ } else if (currentCmd.startsWith(QLatin1String("EPRT"))) {
+ transferConnectionExtended = false;
+ pendingCommands.prepend(QLatin1String("PORT\r\n"));
+ } else {
+ emit error(QFtp::UnknownError, replyText);
+ }
+ if (state != Waiting) {
+ state = Idle;
+ startNextCmd();
+ }
+ break;
+ }
+#if defined(QFTPPI_DEBUG)
+// qDebug("QFtpPI state: %d [processReply() end]", state);
+#endif
+ return true;
+}
+
+/*
+ Starts next pending command. Returns false if there are no pending commands,
+ otherwise it returns true.
+*/
+bool QFtpPI::startNextCmd()
+{
+ if (waitForDtpToConnect)
+ // don't process any new commands until we are connected
+ return true;
+
+#if defined(QFTPPI_DEBUG)
+ if (state != Idle)
+ qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state);
+#endif
+ if (pendingCommands.isEmpty()) {
+ currentCmd.clear();
+ emit finished(replyText);
+ return false;
+ }
+ currentCmd = pendingCommands.first();
+
+ // PORT and PASV are edited in-place, depending on whether we
+ // should try the extended transfer connection commands EPRT and
+ // EPSV. The PORT command also triggers setting up a listener, and
+ // the address/port arguments are edited in.
+ QHostAddress address = commandSocket.localAddress();
+ if (currentCmd.startsWith(QLatin1String("PORT"))) {
+ if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended) {
+ int port = dtp.setupListener(address);
+ currentCmd = QLatin1String("EPRT |");
+ currentCmd += (address.protocol() == QTcpSocket::IPv4Protocol) ? QLatin1Char('1') : QLatin1Char('2');
+ currentCmd += QLatin1Char('|') + address.toString() + QLatin1Char('|') + QString::number(port);
+ currentCmd += QLatin1Char('|');
+ } else if (address.protocol() == QTcpSocket::IPv4Protocol) {
+ int port = dtp.setupListener(address);
+ QString portArg;
+ quint32 ip = address.toIPv4Address();
+ portArg += QString::number((ip & 0xff000000) >> 24);
+ portArg += QLatin1Char(',') + QString::number((ip & 0xff0000) >> 16);
+ portArg += QLatin1Char(',') + QString::number((ip & 0xff00) >> 8);
+ portArg += QLatin1Char(',') + QString::number(ip & 0xff);
+ portArg += QLatin1Char(',') + QString::number((port & 0xff00) >> 8);
+ portArg += QLatin1Char(',') + QString::number(port & 0xff);
+
+ currentCmd = QLatin1String("PORT ");
+ currentCmd += portArg;
+ } else {
+ // No IPv6 connection can be set up with the PORT
+ // command.
+ return false;
+ }
+
+ currentCmd += QLatin1String("\r\n");
+ } else if (currentCmd.startsWith(QLatin1String("PASV"))) {
+ if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended)
+ currentCmd = QLatin1String("EPSV\r\n");
+ }
+
+ pendingCommands.pop_front();
+#if defined(QFTPPI_DEBUG)
+ qDebug("QFtpPI send: %s", currentCmd.left(currentCmd.length()-2).toLatin1().constData());
+#endif
+ state = Waiting;
+ commandSocket.write(currentCmd.toLatin1());
+ return true;
+}
+
+void QFtpPI::dtpConnectState(int s)
+{
+ switch (s) {
+ case QFtpDTP::CsClosed:
+ if (waitForDtpToClose) {
+ // there is an unprocessed reply
+ if (processReply())
+ replyText = QLatin1String("");
+ else
+ return;
+ }
+ waitForDtpToClose = false;
+ readyRead();
+ return;
+ case QFtpDTP::CsConnected:
+ waitForDtpToConnect = false;
+ startNextCmd();
+ return;
+ case QFtpDTP::CsHostNotFound:
+ case QFtpDTP::CsConnectionRefused:
+ emit error(QFtp::ConnectionRefused,
+ QFtp::tr("Connection refused for data connection"));
+ startNextCmd();
+ return;
+ default:
+ return;
+ }
+}
+
+/**********************************************************************
+ *
+ * QFtpPrivate
+ *
+ *********************************************************************/
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <private/qobject_p.h>
+QT_END_INCLUDE_NAMESPACE
+
+class QFtpPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QFtp)
+public:
+
+ inline QFtpPrivate() : close_waitForStateChange(false), state(QFtp::Unconnected),
+ transferMode(QFtp::Passive), error(QFtp::NoError)
+ { }
+
+ ~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }
+
+ // private slots
+ void _q_startNextCommand();
+ void _q_piFinished(const QString&);
+ void _q_piError(int, const QString&);
+ void _q_piConnectState(int);
+ void _q_piFtpReply(int, const QString&);
+
+ int addCommand(QFtpCommand *cmd);
+
+ QFtpPI pi;
+ QList<QFtpCommand *> pending;
+ bool close_waitForStateChange;
+ QFtp::State state;
+ QFtp::TransferMode transferMode;
+ QFtp::Error error;
+ QString errorString;
+
+ QString host;
+ quint16 port;
+ QString proxyHost;
+ quint16 proxyPort;
+};
+
+int QFtpPrivate::addCommand(QFtpCommand *cmd)
+{
+ pending.append(cmd);
+
+ if (pending.count() == 1) {
+ // don't emit the commandStarted() signal before the ID is returned
+ QTimer::singleShot(0, q_func(), SLOT(_q_startNextCommand()));
+ }
+ return cmd->id;
+}
+
+/**********************************************************************
+ *
+ * QFtp implementation
+ *
+ *********************************************************************/
+/*!
+ \class QFtp
+ \brief The QFtp class provides an implementation of the client side of FTP protocol.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+
+ This class provides a direct interface to FTP that allows you to
+ have more control over the requests. However, for new
+ applications, it is recommended to use QNetworkAccessManager and
+ QNetworkReply, as those classes possess a simpler, yet more
+ powerful API.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "commands"
+ in the rest of the documentation) are the following:
+ connectToHost(), login(), close(), list(), cd(), get(), put(),
+ remove(), mkdir(), rmdir(), rename() and rawCommand().
+
+ All of these commands return a unique identifier that allows you
+ to keep track of the command that is currently being executed.
+ When the execution of a command starts, the commandStarted()
+ signal with the command's identifier is emitted. When the command
+ is finished, the commandFinished() signal is emitted with the
+ command's identifier and a bool that indicates whether the command
+ finished with an error.
+
+ In some cases, you might want to execute a sequence of commands,
+ e.g. if you want to connect and login to a FTP server. This is
+ simply achieved:
+
+ \snippet doc/src/snippets/code/src_network_access_qftp.cpp 0
+
+ In this case two FTP commands have been scheduled. When the last
+ scheduled command has finished, a done() signal is emitted with
+ a bool argument that tells you whether the sequence finished with
+ an error.
+
+ If an error occurs during the execution of one of the commands in
+ a sequence of commands, all the pending commands (i.e. scheduled,
+ but not yet executed commands) are cleared and no signals are
+ emitted for them.
+
+ Some commands, e.g. list(), emit additional signals to report
+ their results.
+
+ Example: If you want to download the INSTALL file from the Qt
+ FTP server, you would write this:
+
+ \snippet doc/src/snippets/code/src_network_access_qftp.cpp 1
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_network_access_qftp.cpp 2
+
+ The dataTransferProgress() signal in the above example is useful
+ if you want to show a \link QProgressBar progress bar \endlink to
+ inform the user about the progress of the download. The
+ readyRead() signal tells you that there is data ready to be read.
+ The amount of data can be queried then with the bytesAvailable()
+ function and it can be read with the read() or readAll()
+ function.
+
+ If the login fails for the above example, the signals would look
+ like this:
+
+ \snippet doc/src/snippets/code/src_network_access_qftp.cpp 3
+
+ You can then get details about the error with the error() and
+ errorString() functions.
+
+ For file transfer, QFtp can use both active or passive mode, and
+ it uses passive file transfer mode by default; see the
+ documentation for setTransferMode() for more details about this.
+
+ Call setProxy() to make QFtp connect via an FTP proxy server.
+
+ The functions currentId() and currentCommand() provide more
+ information about the currently executing command.
+
+ The functions hasPendingCommands() and clearPendingCommands()
+ allow you to query and clear the list of pending commands.
+
+ If you are an experienced network programmer and want to have
+ complete control you can use rawCommand() to execute arbitrary FTP
+ commands.
+
+ \warning The current version of QFtp doesn't fully support
+ non-Unix FTP servers.
+
+ \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
+ {FTP Example}
+*/
+
+
+/*!
+ Constructs a QFtp object with the given \a parent.
+*/
+QFtp::QFtp(QObject *parent)
+ : QObject(*new QFtpPrivate, parent)
+{
+ Q_D(QFtp);
+ d->errorString = tr("Unknown error");
+
+ connect(&d->pi, SIGNAL(connectState(int)),
+ SLOT(_q_piConnectState(int)));
+ connect(&d->pi, SIGNAL(finished(QString)),
+ SLOT(_q_piFinished(QString)));
+ connect(&d->pi, SIGNAL(error(int,QString)),
+ SLOT(_q_piError(int,QString)));
+ connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),
+ SLOT(_q_piFtpReply(int,QString)));
+
+ connect(&d->pi.dtp, SIGNAL(readyRead()),
+ SIGNAL(readyRead()));
+ connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),
+ SIGNAL(dataTransferProgress(qint64,qint64)));
+ connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
+ SIGNAL(listInfo(QUrlInfo)));
+}
+
+#ifdef QT3_SUPPORT
+/*!
+ Use one of the constructors that doesn't take the \a name
+ argument and then use setObjectName() instead.
+*/
+QFtp::QFtp(QObject *parent, const char *name)
+ : QObject(*new QFtpPrivate, parent)
+{
+ Q_D(QFtp);
+ setObjectName(QLatin1String(name));
+ d->errorString = tr("Unknown error");
+
+ connect(&d->pi, SIGNAL(connectState(int)),
+ SLOT(_q_piConnectState(int)));
+ connect(&d->pi, SIGNAL(finished(QString)),
+ SLOT(_q_piFinished(QString)));
+ connect(&d->pi, SIGNAL(error(int,QString)),
+ SLOT(_q_piError(int,QString)));
+ connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),
+ SLOT(_q_piFtpReply(int,QString)));
+
+ connect(&d->pi.dtp, SIGNAL(readyRead()),
+ SIGNAL(readyRead()));
+ connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),
+ SIGNAL(dataTransferProgress(qint64,qint64)));
+ connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
+ SIGNAL(listInfo(QUrlInfo)));
+}
+#endif
+
+/*!
+ \enum QFtp::State
+
+ This enum defines the connection state:
+
+ \value Unconnected There is no connection to the host.
+ \value HostLookup A host name lookup is in progress.
+ \value Connecting An attempt to connect to the host is in progress.
+ \value Connected Connection to the host has been achieved.
+ \value LoggedIn Connection and user login have been achieved.
+ \value Closing The connection is closing down, but it is not yet
+ closed. (The state will be \c Unconnected when the connection is
+ closed.)
+
+ \sa stateChanged() state()
+*/
+/*!
+ \enum QFtp::TransferMode
+
+ FTP works with two socket connections; one for commands and
+ another for transmitting data. While the command connection is
+ always initiated by the client, the second connection can be
+ initiated by either the client or the server.
+
+ This enum defines whether the client (Passive mode) or the server
+ (Active mode) should set up the data connection.
+
+ \value Passive The client connects to the server to transmit its
+ data.
+
+ \value Active The server connects to the client to transmit its
+ data.
+*/
+/*!
+ \enum QFtp::TransferType
+
+ This enum identifies the data transfer type used with get and
+ put commands.
+
+ \value Binary The data will be transferred in Binary mode.
+
+ \value Ascii The data will be transferred in Ascii mode and new line
+ characters will be converted to the local format.
+*/
+/*!
+ \enum QFtp::Error
+
+ This enum identifies the error that occurred.
+
+ \value NoError No error occurred.
+ \value HostNotFound The host name lookup failed.
+ \value ConnectionRefused The server refused the connection.
+ \value NotConnected Tried to send a command, but there is no connection to
+ a server.
+ \value UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \enum QFtp::Command
+
+ This enum is used as the return value for the currentCommand() function.
+ This allows you to perform specific actions for particular
+ commands, e.g. in a FTP client, you might want to clear the
+ directory view when a list() command is started; in this case you
+ can simply check in the slot connected to the start() signal if
+ the currentCommand() is \c List.
+
+ \value None No command is being executed.
+ \value SetTransferMode set the \link TransferMode transfer\endlink mode.
+ \value SetProxy switch proxying on or off.
+ \value ConnectToHost connectToHost() is being executed.
+ \value Login login() is being executed.
+ \value Close close() is being executed.
+ \value List list() is being executed.
+ \value Cd cd() is being executed.
+ \value Get get() is being executed.
+ \value Put put() is being executed.
+ \value Remove remove() is being executed.
+ \value Mkdir mkdir() is being executed.
+ \value Rmdir rmdir() is being executed.
+ \value Rename rename() is being executed.
+ \value RawCommand rawCommand() is being executed.
+
+ \sa currentCommand()
+*/
+
+/*!
+ \fn void QFtp::stateChanged(int state)
+
+ This signal is emitted when the state of the connection changes.
+ The argument \a state is the new state of the connection; it is
+ one of the \l State values.
+
+ It is usually emitted in response to a connectToHost() or close()
+ command, but it can also be emitted "spontaneously", e.g. when the
+ server closes the connection unexpectedly.
+
+ \sa connectToHost() close() state() State
+*/
+
+/*!
+ \fn void QFtp::listInfo(const QUrlInfo &i);
+
+ This signal is emitted for each directory entry the list() command
+ finds. The details of the entry are stored in \a i.
+
+ \sa list()
+*/
+
+/*!
+ \fn void QFtp::commandStarted(int id)
+
+ This signal is emitted when processing the command identified by
+ \a id starts.
+
+ \sa commandFinished() done()
+*/
+
+/*!
+ \fn void QFtp::commandFinished(int id, bool error)
+
+ This signal is emitted when processing the command identified by
+ \a id has finished. \a error is true if an error occurred during
+ the processing; otherwise \a error is false.
+
+ \sa commandStarted() done() error() errorString()
+*/
+
+/*!
+ \fn void QFtp::done(bool error)
+
+ This signal is emitted when the last pending command has finished;
+ (it is emitted after the last command's commandFinished() signal).
+ \a error is true if an error occurred during the processing;
+ otherwise \a error is false.
+
+ \sa commandFinished() error() errorString()
+*/
+
+/*!
+ \fn void QFtp::readyRead()
+
+ This signal is emitted in response to a get() command when there
+ is new data to read.
+
+ If you specify a device as the second argument in the get()
+ command, this signal is \e not emitted; instead the data is
+ written directly to the device.
+
+ You can read the data with the readAll() or read() functions.
+
+ This signal is useful if you want to process the data in chunks as
+ soon as it becomes available. If you are only interested in the
+ complete data, just connect to the commandFinished() signal and
+ read the data then instead.
+
+ \sa get() read() readAll() bytesAvailable()
+*/
+
+/*!
+ \fn void QFtp::dataTransferProgress(qint64 done, qint64 total)
+
+ This signal is emitted in response to a get() or put() request to
+ indicate the current progress of the download or upload.
+
+ \a done is the amount of data that has already been transferred
+ and \a total is the total amount of data to be read or written. It
+ is possible that the QFtp class is not able to determine the total
+ amount of data that should be transferred, in which case \a total
+ is 0. (If you connect this signal to a QProgressBar, the progress
+ bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa get(), put(), QProgressBar
+*/
+
+/*!
+ \fn void QFtp::rawCommandReply(int replyCode, const QString &detail);
+
+ This signal is emitted in response to the rawCommand() function.
+ \a replyCode is the 3 digit reply code and \a detail is the text
+ that follows the reply code.
+
+ \sa rawCommand()
+*/
+
+/*!
+ Connects to the FTP server \a host using port \a port.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c HostLookup, then \c
+ Connecting, then \c Connected.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa stateChanged() commandStarted() commandFinished()
+*/
+int QFtp::connectToHost(const QString &host, quint16 port)
+{
+ QStringList cmds;
+ cmds << host;
+ cmds << QString::number((uint)port);
+ int id = d_func()->addCommand(new QFtpCommand(ConnectToHost, cmds));
+ d_func()->pi.transferConnectionExtended = true;
+ return id;
+}
+
+/*!
+ Logs in to the FTP server with the username \a user and the
+ password \a password.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c LoggedIn.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int QFtp::login(const QString &user, const QString &password)
+{
+ QStringList cmds;
+ cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n"));
+ cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n"));
+ return d_func()->addCommand(new QFtpCommand(Login, cmds));
+}
+
+/*!
+ Closes the connection to the FTP server.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c Closing, then \c
+ Unconnected.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa stateChanged() commandStarted() commandFinished()
+*/
+int QFtp::close()
+{
+ return d_func()->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n"))));
+}
+
+/*!
+ Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.
+
+ \sa QFtp::TransferMode
+*/
+int QFtp::setTransferMode(TransferMode mode)
+{
+ int id = d_func()->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
+ d_func()->pi.transferConnectionExtended = true;
+ d_func()->transferMode = mode;
+ return id;
+}
+
+/*!
+ Enables use of the FTP proxy on host \a host and port \a
+ port. Calling this function with \a host empty disables proxying.
+
+ QFtp does not support FTP-over-HTTP proxy servers. Use
+ QNetworkAccessManager for this.
+*/
+int QFtp::setProxy(const QString &host, quint16 port)
+{
+ QStringList args;
+ args << host << QString::number(port);
+ return d_func()->addCommand(new QFtpCommand(SetProxy, args));
+}
+
+/*!
+ Lists the contents of directory \a dir on the FTP server. If \a
+ dir is empty, it lists the contents of the current directory.
+
+ The listInfo() signal is emitted for each directory entry found.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa listInfo() commandStarted() commandFinished()
+*/
+int QFtp::list(const QString &dir)
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE A\r\n");
+ cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+ if (dir.isEmpty())
+ cmds << QLatin1String("LIST\r\n");
+ else
+ cmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));
+ return d_func()->addCommand(new QFtpCommand(List, cmds));
+}
+
+/*!
+ Changes the working directory of the server to \a dir.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int QFtp::cd(const QString &dir)
+{
+ return d_func()->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n"))));
+}
+
+/*!
+ Downloads the file \a file from the server.
+
+ If \a dev is 0, then the readyRead() signal is emitted when there
+ is data available to read. You can then read the data with the
+ read() or readAll() functions.
+
+ If \a dev is not 0, the data is written directly to the device \a
+ dev. Make sure that the \a dev pointer is valid for the duration
+ of the operation (it is safe to delete it when the
+ commandFinished() signal is emitted). In this case the readyRead()
+ signal is \e not emitted and you cannot read data with the
+ read() or readAll() functions.
+
+ If you don't read the data immediately it becomes available, i.e.
+ when the readyRead() signal is emitted, it is still available
+ until the next command is started.
+
+ For example, if you want to present the data to the user as soon
+ as there is something available, connect to the readyRead() signal
+ and read the data immediately. On the other hand, if you only want
+ to work with the complete data, you can connect to the
+ commandFinished() signal and read the data when the get() command
+ is finished.
+
+ The data is transferred as Binary or Ascii depending on the value
+ of \a type.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa readyRead() dataTransferProgress() commandStarted()
+ commandFinished()
+*/
+int QFtp::get(const QString &file, QIODevice *dev, TransferType type)
+{
+ QStringList cmds;
+ cmds << QLatin1String("SIZE ") + file + QLatin1String("\r\n");
+ if (type == Binary)
+ cmds << QLatin1String("TYPE I\r\n");
+ else
+ cmds << QLatin1String("TYPE A\r\n");
+ cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+ cmds << QLatin1String("RETR ") + file + QLatin1String("\r\n");
+ return d_func()->addCommand(new QFtpCommand(Get, cmds, dev));
+}
+
+/*!
+ \overload
+
+ Writes a copy of the given \a data to the file called \a file on
+ the server. The progress of the upload is reported by the
+ dataTransferProgress() signal.
+
+ The data is transferred as Binary or Ascii depending on the value
+ of \a type.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ Since this function takes a copy of the \a data, you can discard
+ your own copy when this function returns.
+
+ \sa dataTransferProgress() commandStarted() commandFinished()
+*/
+int QFtp::put(const QByteArray &data, const QString &file, TransferType type)
+{
+ QStringList cmds;
+ if (type == Binary)
+ cmds << QLatin1String("TYPE I\r\n");
+ else
+ cmds << QLatin1String("TYPE A\r\n");
+ cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+ cmds << QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n");
+ cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
+ return d_func()->addCommand(new QFtpCommand(Put, cmds, data));
+}
+
+/*!
+ Reads the data from the IO device \a dev, and writes it to the
+ file called \a file on the server. The data is read in chunks from
+ the IO device, so this overload allows you to transmit large
+ amounts of data without the need to read all the data into memory
+ at once.
+
+ The data is transferred as Binary or Ascii depending on the value
+ of \a type.
+
+ Make sure that the \a dev pointer is valid for the duration of the
+ operation (it is safe to delete it when the commandFinished() is
+ emitted).
+*/
+int QFtp::put(QIODevice *dev, const QString &file, TransferType type)
+{
+ QStringList cmds;
+ if (type == Binary)
+ cmds << QLatin1String("TYPE I\r\n");
+ else
+ cmds << QLatin1String("TYPE A\r\n");
+ cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
+ if (!dev->isSequential())
+ cmds << QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n");
+ cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
+ return d_func()->addCommand(new QFtpCommand(Put, cmds, dev));
+}
+
+/*!
+ Deletes the file called \a file from the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int QFtp::remove(const QString &file)
+{
+ return d_func()->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n"))));
+}
+
+/*!
+ Creates a directory called \a dir on the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int QFtp::mkdir(const QString &dir)
+{
+ return d_func()->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n"))));
+}
+
+/*!
+ Removes the directory called \a dir from the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int QFtp::rmdir(const QString &dir)
+{
+ return d_func()->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n"))));
+}
+
+/*!
+ Renames the file called \a oldname to \a newname on the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int QFtp::rename(const QString &oldname, const QString &newname)
+{
+ QStringList cmds;
+ cmds << QLatin1String("RNFR ") + oldname + QLatin1String("\r\n");
+ cmds << QLatin1String("RNTO ") + newname + QLatin1String("\r\n");
+ return d_func()->addCommand(new QFtpCommand(Rename, cmds));
+}
+
+/*!
+ Sends the raw FTP command \a command to the FTP server. This is
+ useful for low-level FTP access. If the operation you wish to
+ perform has an equivalent QFtp function, we recommend using the
+ function instead of raw FTP commands since the functions are
+ easier and safer.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa rawCommandReply() commandStarted() commandFinished()
+*/
+int QFtp::rawCommand(const QString &command)
+{
+ QString cmd = command.trimmed() + QLatin1String("\r\n");
+ return d_func()->addCommand(new QFtpCommand(RawCommand, QStringList(cmd)));
+}
+
+/*!
+ Returns the number of bytes that can be read from the data socket
+ at the moment.
+
+ \sa get() readyRead() read() readAll()
+*/
+qint64 QFtp::bytesAvailable() const
+{
+ return d_func()->pi.dtp.bytesAvailable();
+}
+
+/*! \fn qint64 QFtp::readBlock(char *data, quint64 maxlen)
+
+ Use read() instead.
+*/
+
+/*!
+ Reads \a maxlen bytes from the data socket into \a data and
+ returns the number of bytes read. Returns -1 if an error occurred.
+
+ \sa get() readyRead() bytesAvailable() readAll()
+*/
+qint64 QFtp::read(char *data, qint64 maxlen)
+{
+ return d_func()->pi.dtp.read(data, maxlen);
+}
+
+/*!
+ Reads all the bytes available from the data socket and returns
+ them.
+
+ \sa get() readyRead() bytesAvailable() read()
+*/
+QByteArray QFtp::readAll()
+{
+ return d_func()->pi.dtp.readAll();
+}
+
+/*!
+ Aborts the current command and deletes all scheduled commands.
+
+ If there is an unfinished command (i.e. a command for which the
+ commandStarted() signal has been emitted, but for which the
+ commandFinished() signal has not been emitted), this function
+ sends an \c ABORT command to the server. When the server replies
+ that the command is aborted, the commandFinished() signal with the
+ \c error argument set to \c true is emitted for the command. Due
+ to timing issues, it is possible that the command had already
+ finished before the abort request reached the server, in which
+ case, the commandFinished() signal is emitted with the \c error
+ argument set to \c false.
+
+ For all other commands that are affected by the abort(), no
+ signals are emitted.
+
+ If you don't start further FTP commands directly after the
+ abort(), there won't be any scheduled commands and the done()
+ signal is emitted.
+
+ \warning Some FTP servers, for example the BSD FTP daemon (version
+ 0.3), wrongly return a positive reply even when an abort has
+ occurred. For these servers the commandFinished() signal has its
+ error flag set to \c false, even though the command did not
+ complete successfully.
+
+ \sa clearPendingCommands()
+*/
+void QFtp::abort()
+{
+ if (d_func()->pending.isEmpty())
+ return;
+
+ clearPendingCommands();
+ d_func()->pi.abort();
+}
+
+/*!
+ Returns the identifier of the FTP command that is being executed
+ or 0 if there is no command being executed.
+
+ \sa currentCommand()
+*/
+int QFtp::currentId() const
+{
+ if (d_func()->pending.isEmpty())
+ return 0;
+ return d_func()->pending.first()->id;
+}
+
+/*!
+ Returns the command type of the FTP command being executed or \c
+ None if there is no command being executed.
+
+ \sa currentId()
+*/
+QFtp::Command QFtp::currentCommand() const
+{
+ if (d_func()->pending.isEmpty())
+ return None;
+ return d_func()->pending.first()->command;
+}
+
+/*!
+ Returns the QIODevice pointer that is used by the FTP command to read data
+ from or store data to. If there is no current FTP command being executed or
+ if the command does not use an IO device, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the commandFinished() signal.
+
+ \sa get() put()
+*/
+QIODevice* QFtp::currentDevice() const
+{
+ if (d_func()->pending.isEmpty())
+ return 0;
+ QFtpCommand *c = d_func()->pending.first();
+ if (c->is_ba)
+ return 0;
+ return c->data.dev;
+}
+
+/*!
+ Returns true if there are any commands scheduled that have not yet
+ been executed; otherwise returns false.
+
+ The command that is being executed is \e not considered as a
+ scheduled command.
+
+ \sa clearPendingCommands() currentId() currentCommand()
+*/
+bool QFtp::hasPendingCommands() const
+{
+ return d_func()->pending.count() > 1;
+}
+
+/*!
+ Deletes all pending commands from the list of scheduled commands.
+ This does not affect the command that is being executed. If you
+ want to stop this as well, use abort().
+
+ \sa hasPendingCommands() abort()
+*/
+void QFtp::clearPendingCommands()
+{
+ // delete all entires except the first one
+ while (d_func()->pending.count() > 1)
+ delete d_func()->pending.takeLast();
+}
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+QFtp::State QFtp::state() const
+{
+ return d_func()->state;
+}
+
+/*!
+ Returns the last error that occurred. This is useful to find out
+ what went wrong when receiving a commandFinished() or a done()
+ signal with the \c error argument set to \c true.
+
+ If you start a new command, the error status is reset to \c NoError.
+*/
+QFtp::Error QFtp::error() const
+{
+ return d_func()->error;
+}
+
+/*!
+ Returns a human-readable description of the last error that
+ occurred. This is useful for presenting a error message to the
+ user when receiving a commandFinished() or a done() signal with
+ the \c error argument set to \c true.
+
+ The error string is often (but not always) the reply from the
+ server, so it is not always possible to translate the string. If
+ the message comes from Qt, the string has already passed through
+ tr().
+*/
+QString QFtp::errorString() const
+{
+ return d_func()->errorString;
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_startNextCommand()
+{
+ Q_Q(QFtp);
+ if (pending.isEmpty())
+ return;
+ QFtpCommand *c = pending.first();
+
+ error = QFtp::NoError;
+ errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));
+
+ if (q->bytesAvailable())
+ q->readAll(); // clear the data
+ emit q->commandStarted(c->id);
+
+ // Proxy support, replace the Login argument in place, then fall
+ // through.
+ if (c->command == QFtp::Login && !proxyHost.isEmpty()) {
+ QString loginString = c->rawCmds.first().trimmed();
+ loginString += QLatin1Char('@') + host;
+ if (port && port != 21)
+ loginString += QLatin1Char(':') + QString::number(port);
+ loginString += QLatin1String("\r\n");
+ c->rawCmds[0] = loginString;
+ }
+
+ if (c->command == QFtp::SetTransferMode) {
+ _q_piFinished(QLatin1String("Transfer mode set"));
+ } else if (c->command == QFtp::SetProxy) {
+ proxyHost = c->rawCmds[0];
+ proxyPort = c->rawCmds[1].toUInt();
+ 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();
+ pi.connectToHost(proxyHost, proxyPort);
+ } else {
+ pi.connectToHost(c->rawCmds[0], c->rawCmds[1].toUInt());
+ }
+ } else {
+ if (c->command == QFtp::Put) {
+ if (c->is_ba) {
+ pi.dtp.setData(c->data.ba);
+ pi.dtp.setBytesTotal(c->data.ba->size());
+ } else if (c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly))) {
+ pi.dtp.setDevice(c->data.dev);
+ if (c->data.dev->isSequential()) {
+ pi.dtp.setBytesTotal(0);
+ pi.dtp.connect(c->data.dev, SIGNAL(readyRead()), SLOT(dataReadyRead()));
+ pi.dtp.connect(c->data.dev, SIGNAL(readChannelFinished()), SLOT(dataReadyRead()));
+ } else {
+ pi.dtp.setBytesTotal(c->data.dev->size());
+ }
+ }
+ } else if (c->command == QFtp::Get) {
+ if (!c->is_ba && c->data.dev) {
+ pi.dtp.setDevice(c->data.dev);
+ }
+ } else if (c->command == QFtp::Close) {
+ state = QFtp::Closing;
+ emit q->stateChanged(state);
+ }
+ pi.sendCommands(c->rawCmds);
+ }
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piFinished(const QString&)
+{
+ if (pending.isEmpty())
+ return;
+ QFtpCommand *c = pending.first();
+
+ if (c->command == QFtp::Close) {
+ // The order of in which the slots are called is arbitrary, so
+ // disconnect the SIGNAL-SIGNAL temporary to make sure that we
+ // don't get the commandFinished() signal before the stateChanged()
+ // signal.
+ if (state != QFtp::Unconnected) {
+ close_waitForStateChange = true;
+ return;
+ }
+ }
+ emit q_func()->commandFinished(c->id, false);
+ pending.removeFirst();
+
+ delete c;
+
+ if (pending.isEmpty()) {
+ emit q_func()->done(false);
+ } else {
+ _q_startNextCommand();
+ }
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piError(int errorCode, const QString &text)
+{
+ Q_Q(QFtp);
+
+ if (pending.isEmpty()) {
+ qWarning("QFtpPrivate::_q_piError was called without pending command!");
+ return;
+ }
+
+ QFtpCommand *c = pending.first();
+
+ // non-fatal errors
+ if (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {
+ pi.dtp.setBytesTotal(-1);
+ return;
+ } else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {
+ return;
+ }
+
+ error = QFtp::Error(errorCode);
+ switch (q->currentCommand()) {
+ case QFtp::ConnectToHost:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Login:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::List:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Cd:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Get:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Put:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Remove:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Mkdir:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1"))
+ .arg(text);
+ break;
+ case QFtp::Rmdir:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1"))
+ .arg(text);
+ break;
+ default:
+ errorString = text;
+ break;
+ }
+
+ pi.clearPendingCommands();
+ q->clearPendingCommands();
+ emit q->commandFinished(c->id, true);
+
+ pending.removeFirst();
+ delete c;
+ if (pending.isEmpty())
+ emit q->done(true);
+ else
+ _q_startNextCommand();
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piConnectState(int connectState)
+{
+ state = QFtp::State(connectState);
+ emit q_func()->stateChanged(state);
+ if (close_waitForStateChange) {
+ close_waitForStateChange = false;
+ _q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));
+ }
+}
+
+/*! \internal
+*/
+void QFtpPrivate::_q_piFtpReply(int code, const QString &text)
+{
+ if (q_func()->currentCommand() == QFtp::RawCommand) {
+ pi.rawCommand = true;
+ emit q_func()->rawCommandReply(code, text);
+ }
+}
+
+/*!
+ Destructor.
+*/
+QFtp::~QFtp()
+{
+ abort();
+ close();
+}
+
+QT_END_NAMESPACE
+
+#include "qftp.moc"
+
+#include "moc_qftp.cpp"
+
+#endif // QT_NO_FTP
diff --git a/src/network/access/qftp.h b/src/network/access/qftp.h
new file mode 100644
index 0000000000..bee472c3bb
--- /dev/null
+++ b/src/network/access/qftp.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFTP_H
+#define QFTP_H
+
+#include <QtCore/qstring.h>
+#include <QtNetwork/qurlinfo.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_FTP
+
+class QFtpPrivate;
+
+class Q_NETWORK_EXPORT QFtp : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QFtp(QObject *parent = 0);
+ virtual ~QFtp();
+
+ enum State {
+ Unconnected,
+ HostLookup,
+ Connecting,
+ Connected,
+ LoggedIn,
+ Closing
+ };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ NotConnected
+ };
+ enum Command {
+ None,
+ SetTransferMode,
+ SetProxy,
+ ConnectToHost,
+ Login,
+ Close,
+ List,
+ Cd,
+ Get,
+ Put,
+ Remove,
+ Mkdir,
+ Rmdir,
+ Rename,
+ RawCommand
+ };
+ enum TransferMode {
+ Active,
+ Passive
+ };
+ enum TransferType {
+ Binary,
+ Ascii
+ };
+
+ int setProxy(const QString &host, quint16 port);
+ int connectToHost(const QString &host, quint16 port=21);
+ int login(const QString &user = QString(), const QString &password = QString());
+ int close();
+ int setTransferMode(TransferMode mode);
+ int list(const QString &dir = QString());
+ int cd(const QString &dir);
+ int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);
+ int put(const QByteArray &data, const QString &file, TransferType type = Binary);
+ int put(QIODevice *dev, const QString &file, TransferType type = Binary);
+ int remove(const QString &file);
+ int mkdir(const QString &dir);
+ int rmdir(const QString &dir);
+ int rename(const QString &oldname, const QString &newname);
+
+ int rawCommand(const QString &command);
+
+ qint64 bytesAvailable() const;
+ qint64 read(char *data, qint64 maxlen);
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT qint64 readBlock(char *data, quint64 maxlen)
+ { return read(data, qint64(maxlen)); }
+#endif
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice* currentDevice() const;
+ Command currentCommand() const;
+ bool hasPendingCommands() const;
+ void clearPendingCommands();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+Q_SIGNALS:
+ void stateChanged(int);
+ void listInfo(const QUrlInfo&);
+ void readyRead();
+ void dataTransferProgress(qint64, qint64);
+ void rawCommandReply(int, const QString&);
+
+ void commandStarted(int);
+ void commandFinished(int, bool);
+ void done(bool);
+
+#ifdef QT3_SUPPORT
+public:
+ QT3_SUPPORT_CONSTRUCTOR QFtp(QObject *parent, const char *name);
+#endif
+
+private:
+ Q_DISABLE_COPY(QFtp)
+ Q_DECLARE_PRIVATE(QFtp)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_startNextCommand())
+ Q_PRIVATE_SLOT(d_func(), void _q_piFinished(const QString&))
+ Q_PRIVATE_SLOT(d_func(), void _q_piError(int, const QString&))
+ Q_PRIVATE_SLOT(d_func(), void _q_piConnectState(int))
+ Q_PRIVATE_SLOT(d_func(), void _q_piFtpReply(int, const QString&))
+};
+
+#endif // QT_NO_FTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QFTP_H
diff --git a/src/network/access/qhttp.cpp b/src/network/access/qhttp.cpp
new file mode 100644
index 0000000000..291716b174
--- /dev/null
+++ b/src/network/access/qhttp.cpp
@@ -0,0 +1,3155 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHTTP_DEBUG
+
+#include <qplatformdefs.h>
+#include "qhttp.h"
+
+#ifndef QT_NO_HTTP
+# include "private/qobject_p.h"
+# include "qtcpsocket.h"
+# include "qsslsocket.h"
+# include "qtextstream.h"
+# include "qmap.h"
+# include "qlist.h"
+# include "qstring.h"
+# include "qstringlist.h"
+# include "qbuffer.h"
+# include "private/qringbuffer_p.h"
+# include "qcoreevent.h"
+# include "qurl.h"
+# include "qnetworkproxy.h"
+# include "qauthenticator.h"
+# include "qauthenticator_p.h"
+# include "qdebug.h"
+# include "qtimer.h"
+#endif
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNormalRequest;
+class QHttpRequest
+{
+public:
+ QHttpRequest() : finished(false)
+ { id = idCounter.fetchAndAddRelaxed(1); }
+ virtual ~QHttpRequest()
+ { }
+
+ virtual void start(QHttp *) = 0;
+ virtual bool hasRequestHeader();
+ virtual QHttpRequestHeader requestHeader();
+
+ virtual QIODevice *sourceDevice() = 0;
+ virtual QIODevice *destinationDevice() = 0;
+
+ int id;
+ bool finished;
+
+private:
+ static QBasicAtomicInt idCounter;
+};
+
+class QHttpPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QHttp)
+
+ inline QHttpPrivate()
+ : socket(0), reconnectAttempts(2),
+ deleteSocket(0), state(QHttp::Unconnected),
+ error(QHttp::NoError), port(0), mode(QHttp::ConnectionModeHttp),
+ toDevice(0), postDevice(0), bytesDone(0), chunkedSize(-1),
+ repost(false), pendingPost(false)
+ {
+ }
+
+ inline ~QHttpPrivate()
+ {
+ while (!pending.isEmpty())
+ delete pending.takeFirst();
+
+ if (deleteSocket)
+ delete socket;
+ }
+
+ // private slots
+ void _q_startNextRequest();
+ void _q_slotReadyRead();
+ void _q_slotConnected();
+ void _q_slotError(QAbstractSocket::SocketError);
+ void _q_slotClosed();
+ void _q_slotBytesWritten(qint64 numBytes);
+#ifndef QT_NO_OPENSSL
+ void _q_slotEncryptedBytesWritten(qint64 numBytes);
+#endif
+ void _q_slotDoFinished();
+ void _q_slotSendRequest();
+ void _q_continuePost();
+
+ int addRequest(QHttpNormalRequest *);
+ int addRequest(QHttpRequest *);
+ void finishedWithSuccess();
+ void finishedWithError(const QString &detail, int errorCode);
+
+ void init();
+ void setState(int);
+ void closeConn();
+ void setSock(QTcpSocket *sock);
+
+ void postMoreData();
+
+ QTcpSocket *socket;
+ int reconnectAttempts;
+ bool deleteSocket;
+ QList<QHttpRequest *> pending;
+
+ QHttp::State state;
+ QHttp::Error error;
+ QString errorString;
+
+ QString hostName;
+ quint16 port;
+ QHttp::ConnectionMode mode;
+
+ QByteArray buffer;
+ QIODevice *toDevice;
+ QIODevice *postDevice;
+
+ qint64 bytesDone;
+ qint64 bytesTotal;
+ qint64 chunkedSize;
+
+ QHttpRequestHeader header;
+
+ bool readHeader;
+ QString headerStr;
+ QHttpResponseHeader response;
+
+ QRingBuffer rba;
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy;
+ QAuthenticator proxyAuthenticator;
+#endif
+ QAuthenticator authenticator;
+ bool repost;
+ bool hasFinishedWithError;
+ bool pendingPost;
+ QTimer post100ContinueTimer;
+};
+
+QBasicAtomicInt QHttpRequest::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+bool QHttpRequest::hasRequestHeader()
+{
+ return false;
+}
+
+QHttpRequestHeader QHttpRequest::requestHeader()
+{
+ return QHttpRequestHeader();
+}
+
+/****************************************************
+ *
+ * QHttpNormalRequest
+ *
+ ****************************************************/
+
+class QHttpNormalRequest : public QHttpRequest
+{
+public:
+ QHttpNormalRequest(const QHttpRequestHeader &h, QIODevice *d, QIODevice *t) :
+ header(h), to(t)
+ {
+ is_ba = false;
+ data.dev = d;
+ }
+
+ QHttpNormalRequest(const QHttpRequestHeader &h, QByteArray *d, QIODevice *t) :
+ header(h), to(t)
+ {
+ is_ba = true;
+ data.ba = d;
+ }
+
+ ~QHttpNormalRequest()
+ {
+ if (is_ba)
+ delete data.ba;
+ }
+
+ void start(QHttp *);
+ bool hasRequestHeader();
+ QHttpRequestHeader requestHeader();
+ inline void setRequestHeader(const QHttpRequestHeader &h) { header = h; }
+
+ QIODevice *sourceDevice();
+ QIODevice *destinationDevice();
+
+protected:
+ QHttpRequestHeader header;
+
+private:
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+ QIODevice *to;
+};
+
+void QHttpNormalRequest::start(QHttp *http)
+{
+ if (!http->d_func()->socket)
+ http->d_func()->setSock(0);
+ http->d_func()->header = header;
+
+ if (is_ba) {
+ http->d_func()->buffer = *data.ba;
+ if (http->d_func()->buffer.size() >= 0)
+ http->d_func()->header.setContentLength(http->d_func()->buffer.size());
+
+ http->d_func()->postDevice = 0;
+ } else {
+ http->d_func()->buffer = QByteArray();
+
+ if (data.dev && (data.dev->isOpen() || data.dev->open(QIODevice::ReadOnly))) {
+ http->d_func()->postDevice = data.dev;
+ if (http->d_func()->postDevice->size() >= 0)
+ http->d_func()->header.setContentLength(http->d_func()->postDevice->size());
+ } else {
+ http->d_func()->postDevice = 0;
+ }
+ }
+
+ if (to && (to->isOpen() || to->open(QIODevice::WriteOnly)))
+ http->d_func()->toDevice = to;
+ else
+ http->d_func()->toDevice = 0;
+
+ http->d_func()->reconnectAttempts = 2;
+ http->d_func()->_q_slotSendRequest();
+}
+
+bool QHttpNormalRequest::hasRequestHeader()
+{
+ return true;
+}
+
+QHttpRequestHeader QHttpNormalRequest::requestHeader()
+{
+ return header;
+}
+
+QIODevice *QHttpNormalRequest::sourceDevice()
+{
+ if (is_ba)
+ return 0;
+ return data.dev;
+}
+
+QIODevice *QHttpNormalRequest::destinationDevice()
+{
+ return to;
+}
+
+/****************************************************
+ *
+ * QHttpPGHRequest
+ * (like a QHttpNormalRequest, but for the convenience
+ * functions put(), get() and head() -- i.e. set the
+ * host header field correctly before sending the
+ * request)
+ *
+ ****************************************************/
+
+class QHttpPGHRequest : public QHttpNormalRequest
+{
+public:
+ QHttpPGHRequest(const QHttpRequestHeader &h, QIODevice *d, QIODevice *t) :
+ QHttpNormalRequest(h, d, t)
+ { }
+
+ QHttpPGHRequest(const QHttpRequestHeader &h, QByteArray *d, QIODevice *t) :
+ QHttpNormalRequest(h, d, t)
+ { }
+
+ ~QHttpPGHRequest()
+ { }
+
+ void start(QHttp *);
+};
+
+void QHttpPGHRequest::start(QHttp *http)
+{
+ if (http->d_func()->port && http->d_func()->port != 80)
+ header.setValue(QLatin1String("Host"), http->d_func()->hostName + QLatin1Char(':') + QString::number(http->d_func()->port));
+ else
+ header.setValue(QLatin1String("Host"), http->d_func()->hostName);
+ QHttpNormalRequest::start(http);
+}
+
+/****************************************************
+ *
+ * QHttpSetHostRequest
+ *
+ ****************************************************/
+
+class QHttpSetHostRequest : public QHttpRequest
+{
+public:
+ QHttpSetHostRequest(const QString &h, quint16 p, QHttp::ConnectionMode m)
+ : hostName(h), port(p), mode(m)
+ { }
+
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+
+private:
+ QString hostName;
+ quint16 port;
+ QHttp::ConnectionMode mode;
+};
+
+void QHttpSetHostRequest::start(QHttp *http)
+{
+ http->d_func()->hostName = hostName;
+ http->d_func()->port = port;
+ http->d_func()->mode = mode;
+
+#ifdef QT_NO_OPENSSL
+ if (mode == QHttp::ConnectionModeHttps) {
+ // SSL requested but no SSL support compiled in
+ http->d_func()->finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTPS connection requested but SSL support not compiled in")),
+ QHttp::UnknownError);
+ return;
+ }
+#endif
+
+ http->d_func()->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * QHttpSetUserRequest
+ *
+ ****************************************************/
+
+class QHttpSetUserRequest : public QHttpRequest
+{
+public:
+ QHttpSetUserRequest(const QString &userName, const QString &password) :
+ user(userName), pass(password)
+ { }
+
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+
+private:
+ QString user;
+ QString pass;
+};
+
+void QHttpSetUserRequest::start(QHttp *http)
+{
+ http->d_func()->authenticator.setUser(user);
+ http->d_func()->authenticator.setPassword(pass);
+ http->d_func()->finishedWithSuccess();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+
+/****************************************************
+ *
+ * QHttpSetProxyRequest
+ *
+ ****************************************************/
+
+class QHttpSetProxyRequest : public QHttpRequest
+{
+public:
+ inline QHttpSetProxyRequest(const QNetworkProxy &proxy)
+ {
+ this->proxy = proxy;
+ }
+
+ inline void start(QHttp *http)
+ {
+ http->d_func()->proxy = proxy;
+ QString user = proxy.user();
+ if (!user.isEmpty())
+ http->d_func()->proxyAuthenticator.setUser(user);
+ QString password = proxy.password();
+ if (!password.isEmpty())
+ http->d_func()->proxyAuthenticator.setPassword(password);
+ http->d_func()->finishedWithSuccess();
+ }
+
+ inline QIODevice *sourceDevice()
+ { return 0; }
+ inline QIODevice *destinationDevice()
+ { return 0; }
+private:
+ QNetworkProxy proxy;
+};
+
+#endif // QT_NO_NETWORKPROXY
+
+/****************************************************
+ *
+ * QHttpSetSocketRequest
+ *
+ ****************************************************/
+
+class QHttpSetSocketRequest : public QHttpRequest
+{
+public:
+ QHttpSetSocketRequest(QTcpSocket *s) : socket(s)
+ { }
+
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+
+private:
+ QTcpSocket *socket;
+};
+
+void QHttpSetSocketRequest::start(QHttp *http)
+{
+ http->d_func()->setSock(socket);
+ http->d_func()->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * QHttpCloseRequest
+ *
+ ****************************************************/
+
+class QHttpCloseRequest : public QHttpRequest
+{
+public:
+ QHttpCloseRequest()
+ { }
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+};
+
+void QHttpCloseRequest::start(QHttp *http)
+{
+ http->d_func()->closeConn();
+}
+
+class QHttpHeaderPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpHeader)
+public:
+ inline virtual ~QHttpHeaderPrivate() {}
+
+ QList<QPair<QString, QString> > values;
+ bool valid;
+ QHttpHeader *q_ptr;
+};
+
+/****************************************************
+ *
+ * QHttpHeader
+ *
+ ****************************************************/
+
+/*!
+ \class QHttpHeader
+ \obsolete
+ \brief The QHttpHeader class contains header information for HTTP.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ In most cases you should use the more specialized derivatives of
+ this class, QHttpResponseHeader and QHttpRequestHeader, rather
+ than directly using QHttpHeader.
+
+ QHttpHeader provides the HTTP header fields. A HTTP header field
+ consists of a name followed by a colon, a single space, and the
+ field value. (See RFC 1945.) Field names are case-insensitive. A
+ typical header field looks like this:
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 0
+
+ In the API the header field name is called the "key" and the
+ content is called the "value". You can get and set a header
+ field's value by using its key with value() and setValue(), e.g.
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 1
+
+ Some fields are so common that getters and setters are provided
+ for them as a convenient alternative to using \l value() and
+ \l setValue(), e.g. contentLength() and contentType(),
+ setContentLength() and setContentType().
+
+ Each header key has a \e single value associated with it. If you
+ set the value for a key which already exists the previous value
+ will be discarded.
+
+ \sa QHttpRequestHeader QHttpResponseHeader
+*/
+
+/*!
+ \fn int QHttpHeader::majorVersion() const
+
+ Returns the major protocol-version of the HTTP header.
+*/
+
+/*!
+ \fn int QHttpHeader::minorVersion() const
+
+ Returns the minor protocol-version of the HTTP header.
+*/
+
+/*!
+ Constructs an empty HTTP header.
+*/
+QHttpHeader::QHttpHeader()
+ : d_ptr(new QHttpHeaderPrivate)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = true;
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+QHttpHeader::QHttpHeader(const QHttpHeader &header)
+ : d_ptr(new QHttpHeaderPrivate)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = header.d_func()->valid;
+ d->values = header.d_func()->values;
+}
+
+/*!
+ Constructs a HTTP header for \a str.
+
+ This constructor parses the string \a str for header fields and
+ adds this information. The \a str should consist of one or more
+ "\r\n" delimited lines; each of these lines should have the format
+ key, colon, space, value.
+*/
+QHttpHeader::QHttpHeader(const QString &str)
+ : d_ptr(new QHttpHeaderPrivate)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = true;
+ parse(str);
+}
+
+/*! \internal
+ */
+QHttpHeader::QHttpHeader(QHttpHeaderPrivate &dd, const QString &str)
+ : d_ptr(&dd)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = true;
+ if (!str.isEmpty())
+ parse(str);
+}
+
+/*! \internal
+ */
+QHttpHeader::QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header)
+ : d_ptr(&dd)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = header.d_func()->valid;
+ d->values = header.d_func()->values;
+}
+/*!
+ Destructor.
+*/
+QHttpHeader::~QHttpHeader()
+{
+}
+
+/*!
+ Assigns \a h and returns a reference to this http header.
+*/
+QHttpHeader &QHttpHeader::operator=(const QHttpHeader &h)
+{
+ Q_D(QHttpHeader);
+ d->values = h.d_func()->values;
+ d->valid = h.d_func()->valid;
+ return *this;
+}
+
+/*!
+ Returns true if the HTTP header is valid; otherwise returns false.
+
+ A QHttpHeader is invalid if it was created by parsing a malformed string.
+*/
+bool QHttpHeader::isValid() const
+{
+ Q_D(const QHttpHeader);
+ return d->valid;
+}
+
+/*! \internal
+ Parses the HTTP header string \a str for header fields and adds
+ the keys/values it finds. If the string is not parsed successfully
+ the QHttpHeader becomes \link isValid() invalid\endlink.
+
+ Returns true if \a str was successfully parsed; otherwise returns false.
+
+ \sa toString()
+*/
+bool QHttpHeader::parse(const QString &str)
+{
+ Q_D(QHttpHeader);
+ QStringList lst;
+ int pos = str.indexOf(QLatin1Char('\n'));
+ if (pos > 0 && str.at(pos - 1) == QLatin1Char('\r'))
+ lst = str.trimmed().split(QLatin1String("\r\n"));
+ else
+ lst = str.trimmed().split(QLatin1String("\n"));
+ lst.removeAll(QString()); // No empties
+
+ if (lst.isEmpty())
+ return true;
+
+ QStringList lines;
+ QStringList::Iterator it = lst.begin();
+ for (; it != lst.end(); ++it) {
+ if (!(*it).isEmpty()) {
+ if ((*it)[0].isSpace()) {
+ if (!lines.isEmpty()) {
+ lines.last() += QLatin1Char(' ');
+ lines.last() += (*it).trimmed();
+ }
+ } else {
+ lines.append((*it));
+ }
+ }
+ }
+
+ int number = 0;
+ it = lines.begin();
+ for (; it != lines.end(); ++it) {
+ if (!parseLine(*it, number++)) {
+ d->valid = false;
+ return false;
+ }
+ }
+ return true;
+}
+
+/*! \internal
+*/
+void QHttpHeader::setValid(bool v)
+{
+ Q_D(QHttpHeader);
+ d->valid = v;
+}
+
+/*!
+ Returns the first value for the entry with the given \a key. If no entry
+ has this \a key, an empty string is returned.
+
+ \sa setValue() removeValue() hasKey() keys()
+*/
+QString QHttpHeader::value(const QString &key) const
+{
+ Q_D(const QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ if ((*it).first.toLower() == lowercaseKey)
+ return (*it).second;
+ ++it;
+ }
+ return QString();
+}
+
+/*!
+ Returns all the entries with the given \a key. If no entry
+ has this \a key, an empty string list is returned.
+*/
+QStringList QHttpHeader::allValues(const QString &key) const
+{
+ Q_D(const QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QStringList valueList;
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ if ((*it).first.toLower() == lowercaseKey)
+ valueList.append((*it).second);
+ ++it;
+ }
+ return valueList;
+}
+
+/*!
+ Returns a list of the keys in the HTTP header.
+
+ \sa hasKey()
+*/
+QStringList QHttpHeader::keys() const
+{
+ Q_D(const QHttpHeader);
+ QStringList keyList;
+ QSet<QString> seenKeys;
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ const QString &key = (*it).first;
+ QString lowercaseKey = key.toLower();
+ if (!seenKeys.contains(lowercaseKey)) {
+ keyList.append(key);
+ seenKeys.insert(lowercaseKey);
+ }
+ ++it;
+ }
+ return keyList;
+}
+
+/*!
+ Returns true if the HTTP header has an entry with the given \a
+ key; otherwise returns false.
+
+ \sa value() setValue() keys()
+*/
+bool QHttpHeader::hasKey(const QString &key) const
+{
+ Q_D(const QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ if ((*it).first.toLower() == lowercaseKey)
+ return true;
+ ++it;
+ }
+ return false;
+}
+
+/*!
+ Sets the value of the entry with the \a key to \a value.
+
+ If no entry with \a key exists, a new entry with the given \a key
+ and \a value is created. If an entry with the \a key already
+ exists, the first value is discarded and replaced with the given
+ \a value.
+
+ \sa value() hasKey() removeValue()
+*/
+void QHttpHeader::setValue(const QString &key, const QString &value)
+{
+ Q_D(QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+ while (it != d->values.end()) {
+ if ((*it).first.toLower() == lowercaseKey) {
+ (*it).second = value;
+ return;
+ }
+ ++it;
+ }
+ // not found so add
+ addValue(key, value);
+}
+
+/*!
+ Sets the header entries to be the list of key value pairs in \a values.
+*/
+void QHttpHeader::setValues(const QList<QPair<QString, QString> > &values)
+{
+ Q_D(QHttpHeader);
+ d->values = values;
+}
+
+/*!
+ Adds a new entry with the \a key and \a value.
+*/
+void QHttpHeader::addValue(const QString &key, const QString &value)
+{
+ Q_D(QHttpHeader);
+ d->values.append(qMakePair(key, value));
+}
+
+/*!
+ Returns all the entries in the header.
+*/
+QList<QPair<QString, QString> > QHttpHeader::values() const
+{
+ Q_D(const QHttpHeader);
+ return d->values;
+}
+
+/*!
+ Removes the entry with the key \a key from the HTTP header.
+
+ \sa value() setValue()
+*/
+void QHttpHeader::removeValue(const QString &key)
+{
+ Q_D(QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+ while (it != d->values.end()) {
+ if ((*it).first.toLower() == lowercaseKey) {
+ d->values.erase(it);
+ return;
+ }
+ ++it;
+ }
+}
+
+/*!
+ Removes all the entries with the key \a key from the HTTP header.
+*/
+void QHttpHeader::removeAllValues(const QString &key)
+{
+ Q_D(QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+ while (it != d->values.end()) {
+ if ((*it).first.toLower() == lowercaseKey) {
+ it = d->values.erase(it);
+ continue;
+ }
+ ++it;
+ }
+}
+
+/*! \internal
+ Parses the single HTTP header line \a line which has the format
+ key, colon, space, value, and adds key/value to the headers. The
+ linenumber is \a number. Returns true if the line was successfully
+ parsed and the key/value added; otherwise returns false.
+
+ \sa parse()
+*/
+bool QHttpHeader::parseLine(const QString &line, int)
+{
+ int i = line.indexOf(QLatin1Char(':'));
+ if (i == -1)
+ return false;
+
+ addValue(line.left(i).trimmed(), line.mid(i + 1).trimmed());
+
+ return true;
+}
+
+/*!
+ Returns a string representation of the HTTP header.
+
+ The string is suitable for use by the constructor that takes a
+ QString. It consists of lines with the format: key, colon, space,
+ value, "\r\n".
+*/
+QString QHttpHeader::toString() const
+{
+ Q_D(const QHttpHeader);
+ if (!isValid())
+ return QLatin1String("");
+
+ QString ret = QLatin1String("");
+
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ ret += (*it).first + QLatin1String(": ") + (*it).second + QLatin1String("\r\n");
+ ++it;
+ }
+ return ret;
+}
+
+/*!
+ Returns true if the header has an entry for the special HTTP
+ header field \c content-length; otherwise returns false.
+
+ \sa contentLength() setContentLength()
+*/
+bool QHttpHeader::hasContentLength() const
+{
+ return hasKey(QLatin1String("content-length"));
+}
+
+/*!
+ Returns the value of the special HTTP header field \c
+ content-length.
+
+ \sa setContentLength() hasContentLength()
+*/
+uint QHttpHeader::contentLength() const
+{
+ return value(QLatin1String("content-length")).toUInt();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-length
+ to \a len.
+
+ \sa contentLength() hasContentLength()
+*/
+void QHttpHeader::setContentLength(int len)
+{
+ setValue(QLatin1String("content-length"), QString::number(len));
+}
+
+/*!
+ Returns true if the header has an entry for the special HTTP
+ header field \c content-type; otherwise returns false.
+
+ \sa contentType() setContentType()
+*/
+bool QHttpHeader::hasContentType() const
+{
+ return hasKey(QLatin1String("content-type"));
+}
+
+/*!
+ Returns the value of the special HTTP header field \c content-type.
+
+ \sa setContentType() hasContentType()
+*/
+QString QHttpHeader::contentType() const
+{
+ QString type = value(QLatin1String("content-type"));
+ if (type.isEmpty())
+ return QString();
+
+ int pos = type.indexOf(QLatin1Char(';'));
+ if (pos == -1)
+ return type;
+
+ return type.left(pos).trimmed();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-type to
+ \a type.
+
+ \sa contentType() hasContentType()
+*/
+void QHttpHeader::setContentType(const QString &type)
+{
+ setValue(QLatin1String("content-type"), type);
+}
+
+class QHttpResponseHeaderPrivate : public QHttpHeaderPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpResponseHeader)
+public:
+ int statCode;
+ QString reasonPhr;
+ int majVer;
+ int minVer;
+};
+
+/****************************************************
+ *
+ * QHttpResponseHeader
+ *
+ ****************************************************/
+
+/*!
+ \class QHttpResponseHeader
+ \obsolete
+ \brief The QHttpResponseHeader class contains response header information for HTTP.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ This class is used by the QHttp class to report the header
+ information that the client received from the server.
+
+ HTTP responses have a status code that indicates the status of the
+ response. This code is a 3-digit integer result code (for details
+ see to RFC 1945). In addition to the status code, you can also
+ specify a human-readable text that describes the reason for the
+ code ("reason phrase"). This class allows you to get the status
+ code and the reason phrase.
+
+ \sa QHttpRequestHeader, QHttp, {HTTP Example}
+*/
+
+/*!
+ Constructs an empty HTTP response header.
+*/
+QHttpResponseHeader::QHttpResponseHeader()
+ : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+ setValid(false);
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+QHttpResponseHeader::QHttpResponseHeader(const QHttpResponseHeader &header)
+ : QHttpHeader(*new QHttpResponseHeaderPrivate, header)
+{
+ Q_D(QHttpResponseHeader);
+ d->statCode = header.d_func()->statCode;
+ d->reasonPhr = header.d_func()->reasonPhr;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+}
+
+/*!
+ Copies the contents of \a header into this QHttpResponseHeader.
+*/
+QHttpResponseHeader &QHttpResponseHeader::operator=(const QHttpResponseHeader &header)
+{
+ Q_D(QHttpResponseHeader);
+ QHttpHeader::operator=(header);
+ d->statCode = header.d_func()->statCode;
+ d->reasonPhr = header.d_func()->reasonPhr;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+ return *this;
+}
+
+/*!
+ Constructs a HTTP response header from the string \a str. The
+ string is parsed and the information is set. The \a str should
+ consist of one or more "\r\n" delimited lines; the first line should be the
+ status-line (format: HTTP-version, space, status-code, space,
+ reason-phrase); each of remaining lines should have the format key, colon,
+ space, value.
+*/
+QHttpResponseHeader::QHttpResponseHeader(const QString &str)
+ : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+ parse(str);
+}
+
+/*!
+ \since 4.1
+
+ Constructs a QHttpResponseHeader, setting the status code to \a code, the
+ reason phrase to \a text and the protocol-version to \a majorVer and \a
+ minorVer.
+
+ \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+QHttpResponseHeader::QHttpResponseHeader(int code, const QString &text, int majorVer, int minorVer)
+ : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+ setStatusLine(code, text, majorVer, minorVer);
+}
+
+/*!
+ \since 4.1
+
+ Sets the status code to \a code, the reason phrase to \a text and
+ the protocol-version to \a majorVer and \a minorVer.
+
+ \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+void QHttpResponseHeader::setStatusLine(int code, const QString &text, int majorVer, int minorVer)
+{
+ Q_D(QHttpResponseHeader);
+ setValid(true);
+ d->statCode = code;
+ d->reasonPhr = text;
+ d->majVer = majorVer;
+ d->minVer = minorVer;
+}
+
+/*!
+ Returns the status code of the HTTP response header.
+
+ \sa reasonPhrase() majorVersion() minorVersion()
+*/
+int QHttpResponseHeader::statusCode() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->statCode;
+}
+
+/*!
+ Returns the reason phrase of the HTTP response header.
+
+ \sa statusCode() majorVersion() minorVersion()
+*/
+QString QHttpResponseHeader::reasonPhrase() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->reasonPhr;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP response header.
+
+ \sa minorVersion() statusCode() reasonPhrase()
+*/
+int QHttpResponseHeader::majorVersion() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP response header.
+
+ \sa majorVersion() statusCode() reasonPhrase()
+*/
+int QHttpResponseHeader::minorVersion() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->minVer;
+}
+
+/*! \internal
+*/
+bool QHttpResponseHeader::parseLine(const QString &line, int number)
+{
+ Q_D(QHttpResponseHeader);
+ if (number != 0)
+ return QHttpHeader::parseLine(line, number);
+
+ QString l = line.simplified();
+ if (l.length() < 10)
+ return false;
+
+ if (l.left(5) == QLatin1String("HTTP/") && l[5].isDigit() && l[6] == QLatin1Char('.') &&
+ l[7].isDigit() && l[8] == QLatin1Char(' ') && l[9].isDigit()) {
+ d->majVer = l[5].toLatin1() - '0';
+ d->minVer = l[7].toLatin1() - '0';
+
+ int pos = l.indexOf(QLatin1Char(' '), 9);
+ if (pos != -1) {
+ d->reasonPhr = l.mid(pos + 1);
+ d->statCode = l.mid(9, pos - 9).toInt();
+ } else {
+ d->statCode = l.mid(9).toInt();
+ d->reasonPhr.clear();
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+/*! \reimp
+*/
+QString QHttpResponseHeader::toString() const
+{
+ Q_D(const QHttpResponseHeader);
+ QString ret(QLatin1String("HTTP/%1.%2 %3 %4\r\n%5\r\n"));
+ return ret.arg(d->majVer).arg(d->minVer).arg(d->statCode).arg(d->reasonPhr).arg(QHttpHeader::toString());
+}
+
+class QHttpRequestHeaderPrivate : public QHttpHeaderPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpRequestHeader)
+public:
+ QString m;
+ QString p;
+ int majVer;
+ int minVer;
+};
+
+/****************************************************
+ *
+ * QHttpRequestHeader
+ *
+ ****************************************************/
+
+/*!
+ \class QHttpRequestHeader
+ \obsolete
+ \brief The QHttpRequestHeader class contains request header information for HTTP.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ This class is used in the QHttp class to report the header
+ information if the client requests something from the server.
+
+ HTTP requests have a method which describes the request's action.
+ The most common requests are "GET" and "POST". In addition to the
+ request method the header also includes a request-URI to specify
+ the location for the method to use.
+
+ The method, request-URI and protocol-version can be set using a
+ constructor or later using setRequest(). The values can be
+ obtained using method(), path(), majorVersion() and
+ minorVersion().
+
+ Note that the request-URI must be in the format expected by the
+ HTTP server. That is, all reserved characters must be encoded in
+ %HH (where HH are two hexadecimal digits). See
+ QUrl::toPercentEncoding() for more information.
+
+ Important inherited functions: setValue() and value().
+
+ \sa QHttpResponseHeader QHttp
+*/
+
+/*!
+ Constructs an empty HTTP request header.
+*/
+QHttpRequestHeader::QHttpRequestHeader()
+ : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+ setValid(false);
+}
+
+/*!
+ Constructs a HTTP request header for the method \a method, the
+ request-URI \a path and the protocol-version \a majorVer and \a
+ minorVer. The \a path argument must be properly encoded for an
+ HTTP request.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QString &method, const QString &path, int majorVer, int minorVer)
+ : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+ Q_D(QHttpRequestHeader);
+ d->m = method;
+ d->p = path;
+ d->majVer = majorVer;
+ d->minVer = minorVer;
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QHttpRequestHeader &header)
+ : QHttpHeader(*new QHttpRequestHeaderPrivate, header)
+{
+ Q_D(QHttpRequestHeader);
+ d->m = header.d_func()->m;
+ d->p = header.d_func()->p;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+}
+
+/*!
+ Copies the content of \a header into this QHttpRequestHeader
+*/
+QHttpRequestHeader &QHttpRequestHeader::operator=(const QHttpRequestHeader &header)
+{
+ Q_D(QHttpRequestHeader);
+ QHttpHeader::operator=(header);
+ d->m = header.d_func()->m;
+ d->p = header.d_func()->p;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+ return *this;
+}
+
+/*!
+ Constructs a HTTP request header from the string \a str. The \a
+ str should consist of one or more "\r\n" delimited lines; the first line
+ should be the request-line (format: method, space, request-URI, space
+ HTTP-version); each of the remaining lines should have the format key,
+ colon, space, value.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QString &str)
+ : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+ parse(str);
+}
+
+/*!
+ This function sets the request method to \a method, the
+ request-URI to \a path and the protocol-version to \a majorVer and
+ \a minorVer. The \a path argument must be properly encoded for an
+ HTTP request.
+
+ \sa method() path() majorVersion() minorVersion()
+*/
+void QHttpRequestHeader::setRequest(const QString &method, const QString &path, int majorVer, int minorVer)
+{
+ Q_D(QHttpRequestHeader);
+ setValid(true);
+ d->m = method;
+ d->p = path;
+ d->majVer = majorVer;
+ d->minVer = minorVer;
+}
+
+/*!
+ Returns the method of the HTTP request header.
+
+ \sa path() majorVersion() minorVersion() setRequest()
+*/
+QString QHttpRequestHeader::method() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->m;
+}
+
+/*!
+ Returns the request-URI of the HTTP request header.
+
+ \sa method() majorVersion() minorVersion() setRequest()
+*/
+QString QHttpRequestHeader::path() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->p;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP request header.
+
+ \sa minorVersion() method() path() setRequest()
+*/
+int QHttpRequestHeader::majorVersion() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP request header.
+
+ \sa majorVersion() method() path() setRequest()
+*/
+int QHttpRequestHeader::minorVersion() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->minVer;
+}
+
+/*! \internal
+*/
+bool QHttpRequestHeader::parseLine(const QString &line, int number)
+{
+ Q_D(QHttpRequestHeader);
+ if (number != 0)
+ return QHttpHeader::parseLine(line, number);
+
+ QStringList lst = line.simplified().split(QLatin1String(" "));
+ if (lst.count() > 0) {
+ d->m = lst[0];
+ if (lst.count() > 1) {
+ d->p = lst[1];
+ if (lst.count() > 2) {
+ QString v = lst[2];
+ if (v.length() >= 8 && v.left(5) == QLatin1String("HTTP/") &&
+ v[5].isDigit() && v[6] == QLatin1Char('.') && v[7].isDigit()) {
+ d->majVer = v[5].toLatin1() - '0';
+ d->minVer = v[7].toLatin1() - '0';
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/*! \reimp
+*/
+QString QHttpRequestHeader::toString() const
+{
+ Q_D(const QHttpRequestHeader);
+ QString first(QLatin1String("%1 %2"));
+ QString last(QLatin1String(" HTTP/%3.%4\r\n%5\r\n"));
+ return first.arg(d->m).arg(d->p) +
+ last.arg(d->majVer).arg(d->minVer).arg(QHttpHeader::toString());
+}
+
+
+/****************************************************
+ *
+ * QHttp
+ *
+ ****************************************************/
+/*!
+ \class QHttp
+ \obsolete
+ \reentrant
+
+ \brief The QHttp class provides an implementation of the HTTP protocol.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+
+ This class provides a direct interface to HTTP that allows you to
+ download and upload data with the HTTP protocol.
+ However, for new applications, it is
+ recommended to use QNetworkAccessManager and QNetworkReply, as
+ those classes possess a simpler, yet more powerful API
+ and a more modern protocol implementation.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "requests"
+ in the rest of the documentation) are the following: setHost(),
+ get(), post(), head() and request().
+
+ All of these requests return a unique identifier that allows you
+ to keep track of the request that is currently executed. When the
+ execution of a request starts, the requestStarted() signal with
+ the identifier is emitted and when the request is finished, the
+ requestFinished() signal is emitted with the identifier and a bool
+ that indicates if the request finished with an error.
+
+ To make an HTTP request you must set up suitable HTTP headers. The
+ following example demonstrates how to request the main HTML page
+ from the Qt website (i.e., the URL \c http://qt.nokia.com/index.html):
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 2
+
+ For the common HTTP requests \c GET, \c POST and \c HEAD, QHttp
+ provides the convenience functions get(), post() and head(). They
+ already use a reasonable header and if you don't have to set
+ special header fields, they are easier to use. The above example
+ can also be written as:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 3
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 4
+
+ The dataSendProgress() and dataReadProgress() signals in the above
+ example are useful if you want to show a \link QProgressBar
+ progress bar\endlink to inform the user about the progress of the
+ download. The second argument is the total size of data. In
+ certain cases it is not possible to know the total amount in
+ advance, in which case the second argument is 0. (If you connect
+ to a QProgressBar a total of 0 results in a busy indicator.)
+
+ When the response header is read, it is reported with the
+ responseHeaderReceived() signal.
+
+ The readyRead() signal tells you that there is data ready to be
+ read. The amount of data can then be queried with the
+ bytesAvailable() function and it can be read with the read()
+ or readAll() functions.
+
+ If an error occurs during the execution of one of the commands in
+ a sequence of commands, all the pending commands (i.e. scheduled,
+ but not yet executed commands) are cleared and no signals are
+ emitted for them.
+
+ For example, if you have the following sequence of requests
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 5
+
+ and the get() request fails because the host lookup fails, then
+ the post() request is never executed and the signals would look
+ like this:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 6
+
+ You can then get details about the error with the error() and
+ errorString() functions. Note that only unexpected behavior, like
+ network failure is considered as an error. If the server response
+ contains an error status, like a 404 response, this is reported as
+ a normal response case. So you should always check the \link
+ QHttpResponseHeader::statusCode() status code \endlink of the
+ response header.
+
+ The functions currentId() and currentRequest() provide more
+ information about the currently executing request.
+
+ The functions hasPendingRequests() and clearPendingRequests()
+ allow you to query and clear the list of pending requests.
+
+ \sa QFtp, QNetworkAccessManager, QNetworkRequest, QNetworkReply,
+ {HTTP Example}, {Torrent Example}
+*/
+
+/*!
+ Constructs a QHttp object. The \a parent parameter is passed on
+ to the QObject constructor.
+*/
+QHttp::QHttp(QObject *parent)
+ : QObject(*new QHttpPrivate, parent)
+{
+ Q_D(QHttp);
+ d->init();
+}
+
+/*!
+ Constructs a QHttp object. Subsequent requests are done by
+ connecting to the server \a hostName on port \a port.
+
+ The \a parent parameter is passed on to the QObject constructor.
+
+ \sa setHost()
+*/
+QHttp::QHttp(const QString &hostName, quint16 port, QObject *parent)
+ : QObject(*new QHttpPrivate, parent)
+{
+ Q_D(QHttp);
+ d->init();
+
+ d->hostName = hostName;
+ d->port = port;
+}
+
+/*!
+ Constructs a QHttp object. Subsequent requests are done by
+ connecting to the server \a hostName on port \a port using the
+ connection mode \a mode.
+
+ If port is 0, it will use the default port for the \a mode used
+ (80 for Http and 443 for Https).
+
+ The \a parent parameter is passed on to the QObject constructor.
+
+ \sa setHost()
+*/
+QHttp::QHttp(const QString &hostName, ConnectionMode mode, quint16 port, QObject *parent)
+ : QObject(*new QHttpPrivate, parent)
+{
+ Q_D(QHttp);
+ d->init();
+
+ d->hostName = hostName;
+ if (port == 0)
+ port = (mode == ConnectionModeHttp) ? 80 : 443;
+ d->port = port;
+ d->mode = mode;
+}
+
+void QHttpPrivate::init()
+{
+ Q_Q(QHttp);
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown error"));
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+ post100ContinueTimer.setSingleShot(true);
+ QObject::connect(&post100ContinueTimer, SIGNAL(timeout()), q, SLOT(_q_continuePost()));
+}
+
+/*!
+ Destroys the QHttp object. If there is an open connection, it is
+ closed.
+*/
+QHttp::~QHttp()
+{
+ abort();
+}
+
+/*!
+ \enum QHttp::ConnectionMode
+ \since 4.3
+
+ This enum is used to specify the mode of connection to use:
+
+ \value ConnectionModeHttp The connection is a regular HTTP connection to the server
+ \value ConnectionModeHttps The HTTPS protocol is used and the connection is encrypted using SSL.
+
+ When using the HTTPS mode, care should be taken to connect to the sslErrors signal, and
+ handle possible SSL errors.
+
+ \sa QSslSocket
+*/
+
+/*!
+ \enum QHttp::State
+
+ This enum is used to specify the state the client is in:
+
+ \value Unconnected There is no connection to the host.
+ \value HostLookup A host name lookup is in progress.
+ \value Connecting An attempt to connect to the host is in progress.
+ \value Sending The client is sending its request to the server.
+ \value Reading The client's request has been sent and the client
+ is reading the server's response.
+ \value Connected The connection to the host is open, but the client is
+ neither sending a request, nor waiting for a response.
+ \value Closing The connection is closing down, but is not yet
+ closed. (The state will be \c Unconnected when the connection is
+ closed.)
+
+ \sa stateChanged() state()
+*/
+
+/*! \enum QHttp::Error
+
+ This enum identifies the error that occurred.
+
+ \value NoError No error occurred.
+ \value HostNotFound The host name lookup failed.
+ \value ConnectionRefused The server refused the connection.
+ \value UnexpectedClose The server closed the connection unexpectedly.
+ \value InvalidResponseHeader The server sent an invalid response header.
+ \value WrongContentLength The client could not read the content correctly
+ because an error with respect to the content length occurred.
+ \value Aborted The request was aborted with abort().
+ \value ProxyAuthenticationRequiredError QHttp is using a proxy, and the
+ proxy server requires authentication to establish a connection.
+ \value AuthenticationRequiredError The web server requires authentication
+ to complete the request.
+ \value UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \fn void QHttp::stateChanged(int state)
+
+ This signal is emitted when the state of the QHttp object changes.
+ The argument \a state is the new state of the connection; it is
+ one of the \l State values.
+
+ This usually happens when a request is started, but it can also
+ happen when the server closes the connection or when a call to
+ close() succeeded.
+
+ \sa get() post() head() request() close() state() State
+*/
+
+/*!
+ \fn void QHttp::responseHeaderReceived(const QHttpResponseHeader &resp);
+
+ This signal is emitted when the HTTP header of a server response
+ is available. The header is passed in \a resp.
+
+ \sa get() post() head() request() readyRead()
+*/
+
+/*!
+ \fn void QHttp::readyRead(const QHttpResponseHeader &resp)
+
+ This signal is emitted when there is new response data to read.
+
+ If you specified a device in the request where the data should be
+ written to, then this signal is \e not emitted; instead the data
+ is written directly to the device.
+
+ The response header is passed in \a resp.
+
+ You can read the data with the readAll() or read() functions
+
+ This signal is useful if you want to process the data in chunks as
+ soon as it becomes available. If you are only interested in the
+ complete data, just connect to the requestFinished() signal and
+ read the data then instead.
+
+ \sa get() post() request() readAll() read() bytesAvailable()
+*/
+
+/*!
+ \fn void QHttp::dataSendProgress(int done, int total)
+
+ This signal is emitted when this object sends data to a HTTP
+ server to inform it about the progress of the upload.
+
+ \a done is the amount of data that has already arrived and \a
+ total is the total amount of data. It is possible that the total
+ amount of data that should be transferred cannot be determined, in
+ which case \a total is 0.(If you connect to a QProgressBar, the
+ progress bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa dataReadProgress(), post(), request(), QProgressBar
+*/
+
+/*!
+ \fn void QHttp::dataReadProgress(int done, int total)
+
+ This signal is emitted when this object reads data from a HTTP
+ server to indicate the current progress of the download.
+
+ \a done is the amount of data that has already arrived and \a
+ total is the total amount of data. It is possible that the total
+ amount of data that should be transferred cannot be determined, in
+ which case \a total is 0.(If you connect to a QProgressBar, the
+ progress bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa dataSendProgress() get() post() request() QProgressBar
+*/
+
+/*!
+ \fn void QHttp::requestStarted(int id)
+
+ This signal is emitted when processing the request identified by
+ \a id starts.
+
+ \sa requestFinished() done()
+*/
+
+/*!
+ \fn void QHttp::requestFinished(int id, bool error)
+
+ This signal is emitted when processing the request identified by
+ \a id has finished. \a error is true if an error occurred during
+ the processing; otherwise \a error is false.
+
+ \sa requestStarted() done() error() errorString()
+*/
+
+/*!
+ \fn void QHttp::done(bool error)
+
+ This signal is emitted when the last pending request has finished;
+ (it is emitted after the last request's requestFinished() signal).
+ \a error is true if an error occurred during the processing;
+ otherwise \a error is false.
+
+ \sa requestFinished() error() errorString()
+*/
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*!
+ \fn void QHttp::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
+ \since 4.3
+
+ This signal can be emitted when a \a proxy that requires
+ authentication is used. The \a authenticator object can then be
+ filled in with the required details to allow authentication and
+ continue the connection.
+
+ \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 QAuthenticator, QNetworkProxy
+*/
+
+#endif
+
+/*!
+ \fn void QHttp::authenticationRequired(const QString &hostname, quint16 port, QAuthenticator *authenticator)
+ \since 4.3
+
+ This signal can be emitted when a web server on a given \a hostname and \a
+ port requires authentication. The \a authenticator object can then be
+ filled in with the required details to allow authentication and continue
+ the connection.
+
+ \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 QAuthenticator, QNetworkProxy
+*/
+
+/*!
+ \fn void QHttp::sslErrors(const QList<QSslError> &errors)
+ \since 4.3
+
+ Forwards the sslErrors signal from the QSslSocket used in QHttp. \a errors
+ is the list of errors that occurred during the SSL handshake. Unless you
+ call ignoreSslErrors() from within a slot connected to this signal when an
+ error occurs, QHttp will tear down the connection immediately after
+ emitting the signal.
+
+ \sa QSslSocket QSslSocket::ignoreSslErrors()
+*/
+
+/*!
+ Aborts the current request and deletes all scheduled requests.
+
+ For the current request, the requestFinished() signal with the \c
+ error argument \c true is emitted. For all other requests that are
+ affected by the abort(), no signals are emitted.
+
+ Since this slot also deletes the scheduled requests, there are no
+ requests left and the done() signal is emitted (with the \c error
+ argument \c true).
+
+ \sa clearPendingRequests()
+*/
+void QHttp::abort()
+{
+ Q_D(QHttp);
+ if (d->pending.isEmpty())
+ return;
+
+ d->finishedWithError(tr("Request aborted"), Aborted);
+ clearPendingRequests();
+ if (d->socket)
+ d->socket->abort();
+ d->closeConn();
+}
+
+/*!
+ Returns the number of bytes that can be read from the response
+ content at the moment.
+
+ \sa get() post() request() readyRead() read() readAll()
+*/
+qint64 QHttp::bytesAvailable() const
+{
+ Q_D(const QHttp);
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::bytesAvailable(): %d bytes", (int)d->rba.size());
+#endif
+ return qint64(d->rba.size());
+}
+
+/*! \fn qint64 QHttp::readBlock(char *data, quint64 maxlen)
+
+ Use read() instead.
+*/
+
+/*!
+ Reads \a maxlen bytes from the response content into \a data and
+ returns the number of bytes read. Returns -1 if an error occurred.
+
+ \sa get() post() request() readyRead() bytesAvailable() readAll()
+*/
+qint64 QHttp::read(char *data, qint64 maxlen)
+{
+ Q_D(QHttp);
+ if (data == 0 && maxlen != 0) {
+ qWarning("QHttp::read: Null pointer error");
+ return -1;
+ }
+ if (maxlen >= d->rba.size())
+ maxlen = d->rba.size();
+ int readSoFar = 0;
+ while (!d->rba.isEmpty() && readSoFar < maxlen) {
+ int nextBlockSize = d->rba.nextDataBlockSize();
+ int bytesToRead = qMin<qint64>(maxlen - readSoFar, nextBlockSize);
+ memcpy(data + readSoFar, d->rba.readPointer(), bytesToRead);
+ d->rba.free(bytesToRead);
+ readSoFar += bytesToRead;
+ }
+
+ d->bytesDone += maxlen;
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::read(): read %lld bytes (%lld bytes done)", maxlen, d->bytesDone);
+#endif
+ return maxlen;
+}
+
+/*!
+ Reads all the bytes from the response content and returns them.
+
+ \sa get() post() request() readyRead() bytesAvailable() read()
+*/
+QByteArray QHttp::readAll()
+{
+ qint64 avail = bytesAvailable();
+ QByteArray tmp;
+ tmp.resize(int(avail));
+ qint64 got = read(tmp.data(), int(avail));
+ tmp.resize(got);
+ return tmp;
+}
+
+/*!
+ Returns the identifier of the HTTP request being executed or 0 if
+ there is no request being executed (i.e. they've all finished).
+
+ \sa currentRequest()
+*/
+int QHttp::currentId() const
+{
+ Q_D(const QHttp);
+ if (d->pending.isEmpty())
+ return 0;
+ return d->pending.first()->id;
+}
+
+/*!
+ Returns the request header of the HTTP request being executed. If
+ the request is one issued by setHost() or close(), it
+ returns an invalid request header, i.e.
+ QHttpRequestHeader::isValid() returns false.
+
+ \sa currentId()
+*/
+QHttpRequestHeader QHttp::currentRequest() const
+{
+ Q_D(const QHttp);
+ if (!d->pending.isEmpty()) {
+ QHttpRequest *r = d->pending.first();
+ if (r->hasRequestHeader())
+ return r->requestHeader();
+ }
+ return QHttpRequestHeader();
+}
+
+/*!
+ Returns the received response header of the most recently finished HTTP
+ request. If no response has yet been received
+ QHttpResponseHeader::isValid() will return false.
+
+ \sa currentRequest()
+*/
+QHttpResponseHeader QHttp::lastResponse() const
+{
+ Q_D(const QHttp);
+ return d->response;
+}
+
+/*!
+ Returns the QIODevice pointer that is used as the data source of the HTTP
+ request being executed. If there is no current request or if the request
+ does not use an IO device as the data source, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentDestinationDevice() post() request()
+*/
+QIODevice *QHttp::currentSourceDevice() const
+{
+ Q_D(const QHttp);
+ if (d->pending.isEmpty())
+ return 0;
+ return d->pending.first()->sourceDevice();
+}
+
+/*!
+ Returns the QIODevice pointer that is used as to store the data of the HTTP
+ request being executed. If there is no current request or if the request
+ does not store the data to an IO device, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentSourceDevice() get() post() request()
+*/
+QIODevice *QHttp::currentDestinationDevice() const
+{
+ Q_D(const QHttp);
+ if (d->pending.isEmpty())
+ return 0;
+ return d->pending.first()->destinationDevice();
+}
+
+/*!
+ Returns true if there are any requests scheduled that have not yet
+ been executed; otherwise returns false.
+
+ The request that is being executed is \e not considered as a
+ scheduled request.
+
+ \sa clearPendingRequests() currentId() currentRequest()
+*/
+bool QHttp::hasPendingRequests() const
+{
+ Q_D(const QHttp);
+ return d->pending.count() > 1;
+}
+
+/*!
+ Deletes all pending requests from the list of scheduled requests.
+ This does not affect the request that is being executed. If
+ you want to stop this as well, use abort().
+
+ \sa hasPendingRequests() abort()
+*/
+void QHttp::clearPendingRequests()
+{
+ Q_D(QHttp);
+ // delete all entires except the first one
+ while (d->pending.count() > 1)
+ delete d->pending.takeLast();
+}
+
+/*!
+ Sets the HTTP server that is used for requests to \a hostName on
+ port \a port.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::setHost(const QString &hostName, quint16 port)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetHostRequest(hostName, port, ConnectionModeHttp));
+}
+
+/*!
+ Sets the HTTP server that is used for requests to \a hostName on
+ port \a port using the connection mode \a mode.
+
+ If port is 0, it will use the default port for the \a mode used
+ (80 for HTTP and 443 for HTTPS).
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::setHost(const QString &hostName, ConnectionMode mode, quint16 port)
+{
+#ifdef QT_NO_OPENSSL
+ if (mode == ConnectionModeHttps)
+ qWarning("QHttp::setHost: HTTPS connection requested but SSL support not compiled in");
+#endif
+ Q_D(QHttp);
+ if (port == 0)
+ port = (mode == ConnectionModeHttp) ? 80 : 443;
+ return d->addRequest(new QHttpSetHostRequest(hostName, port, mode));
+}
+
+/*!
+ Replaces the internal QTcpSocket that QHttp uses with \a
+ socket. This is useful if you want to use your own custom QTcpSocket
+ subclass instead of the plain QTcpSocket that QHttp uses by default.
+ QHttp does not take ownership of the socket, and will not delete \a
+ socket when destroyed.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ Note: If QHttp is used in a non-GUI thread that runs its own event
+ loop, you must move \a socket to that thread before calling setSocket().
+
+ \sa QObject::moveToThread(), {Thread Support in Qt}
+*/
+int QHttp::setSocket(QTcpSocket *socket)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetSocketRequest(socket));
+}
+
+/*!
+ This function sets the user name \a userName and password \a
+ password for web pages that require authentication.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+*/
+int QHttp::setUser(const QString &userName, const QString &password)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetUserRequest(userName, password));
+}
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*!
+ Enables HTTP proxy support, using the proxy server \a host on port \a
+ port. \a username and \a password can be provided if the proxy server
+ requires authentication.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 7
+
+ QHttp supports non-transparent web proxy servers only, such as the Squid
+ Web proxy cache server (from \l http://www.squid.org/). For transparent
+ proxying, such as SOCKS5, use QNetworkProxy instead.
+
+ \note setProxy() has to be called before setHost() for it to take effect.
+ If setProxy() is called after setHost(), then it will not apply until after
+ setHost() is called again.
+
+ \sa QFtp::setProxy()
+*/
+int QHttp::setProxy(const QString &host, int port,
+ const QString &username, const QString &password)
+{
+ Q_D(QHttp);
+ QNetworkProxy proxy(QNetworkProxy::HttpProxy, host, port, username, password);
+ return d->addRequest(new QHttpSetProxyRequest(proxy));
+}
+
+/*!
+ \overload
+
+ Enables HTTP proxy support using the proxy settings from \a
+ proxy. If \a proxy is a transparent proxy, QHttp will call
+ QAbstractSocket::setProxy() on the underlying socket. If the type
+ is QNetworkProxy::HttpCachingProxy, QHttp will behave like the
+ previous function.
+
+ \note for compatibility with Qt 4.3, if the proxy type is
+ QNetworkProxy::HttpProxy and the request type is unencrypted (that
+ is, ConnectionModeHttp), QHttp will treat the proxy as a caching
+ proxy.
+*/
+int QHttp::setProxy(const QNetworkProxy &proxy)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetProxyRequest(proxy));
+}
+
+#endif
+
+/*!
+ Sends a get request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be a absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html and
+ must be encoded with either QUrl::toPercentEncoding() or
+ QUrl::encodedPath().
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ \section1 Request Processing
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost(), post(), head(), request(), requestStarted(),
+ requestFinished(), done()
+*/
+int QHttp::get(const QString &path, QIODevice *to)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("GET"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, (QIODevice *) 0, to));
+}
+
+/*!
+ Sends a post request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html and
+ must be encoded with either QUrl::toPercentEncoding() or
+ QUrl::encodedPath().
+
+ The incoming data comes via the \a data IO device.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::post(const QString &path, QIODevice *data, QIODevice *to )
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("POST"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, data, to));
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int QHttp::post(const QString &path, const QByteArray &data, QIODevice *to)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("POST"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, new QByteArray(data), to));
+}
+
+/*!
+ Sends a header request for \a path to the server set by setHost()
+ or as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() post() request() requestStarted() requestFinished() done()
+*/
+int QHttp::head(const QString &path)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("HEAD"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, (QIODevice*)0, 0));
+}
+
+/*!
+ Sends a request to the server set by setHost() or as specified in
+ the constructor. Uses the \a header as the HTTP request header.
+ You are responsible for setting up a header that is appropriate
+ for your request.
+
+ The incoming data comes via the \a data IO device.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() post() head() requestStarted() requestFinished() done()
+*/
+int QHttp::request(const QHttpRequestHeader &header, QIODevice *data, QIODevice *to)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpNormalRequest(header, data, to));
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int QHttp::request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to )
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpNormalRequest(header, new QByteArray(data), to));
+}
+
+/*!
+ Closes the connection; this is useful if you have a keep-alive
+ connection and want to close it.
+
+ For the requests issued with get(), post() and head(), QHttp sets
+ the connection to be keep-alive. You can also do this using the
+ header you pass to the request() function. QHttp only closes the
+ connection to the HTTP server if the response header requires it
+ to do so.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ If you want to close the connection immediately, you have to use
+ abort() instead.
+
+ \sa stateChanged() abort() requestStarted() requestFinished() done()
+*/
+int QHttp::close()
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpCloseRequest());
+}
+
+/*!
+ \obsolete
+
+ Behaves the same as close().
+*/
+int QHttp::closeConnection()
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpCloseRequest());
+}
+
+int QHttpPrivate::addRequest(QHttpNormalRequest *req)
+{
+ QHttpRequestHeader h = req->requestHeader();
+ if (h.path().isEmpty()) {
+ // note: the following qWarning is autotested. If you change it, change the test too.
+ qWarning("QHttp: empty path requested is invalid -- using '/'");
+ h.setRequest(h.method(), QLatin1String("/"), h.majorVersion(), h.minorVersion());
+ req->setRequestHeader(h);
+ }
+
+ // contine below
+ return addRequest(static_cast<QHttpRequest *>(req));
+}
+
+int QHttpPrivate::addRequest(QHttpRequest *req)
+{
+ Q_Q(QHttp);
+ pending.append(req);
+
+ if (pending.count() == 1) {
+ // don't emit the requestStarted() signal before the id is returned
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+ return req->id;
+}
+
+void QHttpPrivate::_q_startNextRequest()
+{
+ Q_Q(QHttp);
+ if (pending.isEmpty())
+ return;
+ QHttpRequest *r = pending.first();
+
+ error = QHttp::NoError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown error"));
+
+ if (q->bytesAvailable() != 0)
+ q->readAll(); // clear the data
+ emit q->requestStarted(r->id);
+ r->start(q);
+}
+
+void QHttpPrivate::_q_slotSendRequest()
+{
+ if (hostName.isNull()) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "No server set to connect to")),
+ QHttp::UnknownError);
+ return;
+ }
+
+ QString connectionHost = hostName;
+ int connectionPort = port;
+ bool sslInUse = false;
+
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
+ if (mode == QHttp::ConnectionModeHttps || (sslSocket && sslSocket->isEncrypted()))
+ sslInUse = true;
+#endif
+
+#ifndef QT_NO_NETWORKPROXY
+ bool cachingProxyInUse = false;
+ bool transparentProxyInUse = false;
+ if (proxy.type() == QNetworkProxy::DefaultProxy)
+ proxy = QNetworkProxy::applicationProxy();
+
+ if (proxy.type() == QNetworkProxy::HttpCachingProxy) {
+ if (proxy.hostName().isEmpty())
+ proxy.setType(QNetworkProxy::NoProxy);
+ else
+ cachingProxyInUse = true;
+ } else if (proxy.type() == QNetworkProxy::HttpProxy) {
+ // Compatibility behaviour: HttpProxy can be used to mean both
+ // transparent and caching proxy
+ if (proxy.hostName().isEmpty()) {
+ proxy.setType(QNetworkProxy::NoProxy);
+ } else if (sslInUse) {
+ // Disallow use of caching proxy with HTTPS; instead fall back to
+ // transparent HTTP CONNECT proxying.
+ transparentProxyInUse = true;
+ } else {
+ proxy.setType(QNetworkProxy::HttpCachingProxy);
+ cachingProxyInUse = true;
+ }
+ }
+
+ // Proxy support. Insert the Proxy-Authorization item into the
+ // header before it's sent off to the proxy.
+ if (cachingProxyInUse) {
+ QUrl proxyUrl;
+ proxyUrl.setScheme(QLatin1String("http"));
+ proxyUrl.setHost(hostName);
+ if (port && port != 80)
+ proxyUrl.setPort(port);
+ QString request = QString::fromAscii(proxyUrl.resolved(QUrl::fromEncoded(header.path().toLatin1())).toEncoded());
+
+ header.setRequest(header.method(), request, header.majorVersion(), header.minorVersion());
+ header.setValue(QLatin1String("Proxy-Connection"), QLatin1String("keep-alive"));
+
+ QAuthenticatorPrivate *auth = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
+ if (auth && auth->method != QAuthenticatorPrivate::None) {
+ QByteArray response = auth->calculateResponse(header.method().toLatin1(), header.path().toLatin1());
+ header.setValue(QLatin1String("Proxy-Authorization"), QString::fromLatin1(response));
+ }
+
+ connectionHost = proxy.hostName();
+ connectionPort = proxy.port();
+ }
+
+ if (transparentProxyInUse || sslInUse) {
+ socket->setProxy(proxy);
+ }
+#endif
+
+ // Username support. Insert the user and password into the query
+ // string.
+ QAuthenticatorPrivate *auth = QAuthenticatorPrivate::getPrivate(authenticator);
+ if (auth && auth->method != QAuthenticatorPrivate::None) {
+ QByteArray response = auth->calculateResponse(header.method().toLatin1(), header.path().toLatin1());
+ header.setValue(QLatin1String("Authorization"), QString::fromLatin1(response));
+ }
+
+ // Do we need to setup a new connection or can we reuse an
+ // existing one?
+ if (socket->peerName() != connectionHost || socket->peerPort() != connectionPort
+ || socket->state() != QTcpSocket::ConnectedState
+#ifndef QT_NO_OPENSSL
+ || (sslSocket && sslSocket->isEncrypted() != (mode == QHttp::ConnectionModeHttps))
+#endif
+ ) {
+ socket->blockSignals(true);
+ socket->abort();
+ socket->blockSignals(false);
+
+ setState(QHttp::Connecting);
+#ifndef QT_NO_OPENSSL
+ if (sslSocket && mode == QHttp::ConnectionModeHttps) {
+ sslSocket->connectToHostEncrypted(hostName, port);
+ } else
+#endif
+ {
+ socket->connectToHost(connectionHost, connectionPort);
+ }
+ } else {
+ _q_slotConnected();
+ }
+
+}
+
+void QHttpPrivate::finishedWithSuccess()
+{
+ Q_Q(QHttp);
+ if (pending.isEmpty())
+ return;
+ QHttpRequest *r = pending.first();
+
+ // did we recurse?
+ if (r->finished)
+ return;
+ r->finished = true;
+ hasFinishedWithError = false;
+
+ emit q->requestFinished(r->id, false);
+ if (hasFinishedWithError) {
+ // we recursed and changed into an error. The finishedWithError function
+ // below has emitted the done(bool) signal and cleared the queue by now.
+ return;
+ }
+
+ pending.removeFirst();
+ delete r;
+
+ if (pending.isEmpty()) {
+ emit q->done(false);
+ } else {
+ _q_startNextRequest();
+ }
+}
+
+void QHttpPrivate::finishedWithError(const QString &detail, int errorCode)
+{
+ Q_Q(QHttp);
+ if (pending.isEmpty())
+ return;
+ QHttpRequest *r = pending.first();
+ hasFinishedWithError = true;
+
+ error = QHttp::Error(errorCode);
+ errorString = detail;
+
+ // did we recurse?
+ if (!r->finished) {
+ r->finished = true;
+ emit q->requestFinished(r->id, true);
+ }
+
+ while (!pending.isEmpty())
+ delete pending.takeFirst();
+ emit q->done(hasFinishedWithError);
+}
+
+void QHttpPrivate::_q_slotClosed()
+{
+ Q_Q(QHttp);
+
+ if (state == QHttp::Reading) {
+ if (response.hasKey(QLatin1String("content-length"))) {
+ // We got Content-Length, so did we get all bytes?
+ if (bytesDone + q->bytesAvailable() != response.contentLength()) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Wrong content length")), QHttp::WrongContentLength);
+ }
+ }
+ } else if (state == QHttp::Connecting || state == QHttp::Sending) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Server closed connection unexpectedly")), QHttp::UnexpectedClose);
+ }
+
+ postDevice = 0;
+ if (state != QHttp::Closing)
+ setState(QHttp::Closing);
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+}
+
+void QHttpPrivate::_q_continuePost()
+{
+ if (pendingPost) {
+ pendingPost = false;
+ setState(QHttp::Sending);
+ _q_slotBytesWritten(0);
+ }
+}
+
+void QHttpPrivate::_q_slotConnected()
+{
+ if (state != QHttp::Sending) {
+ bytesDone = 0;
+ setState(QHttp::Sending);
+ }
+
+ QString str = header.toString();
+ bytesTotal = str.length();
+ socket->write(str.toLatin1(), bytesTotal);
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp: write request header %p:\n---{\n%s}---", &header, str.toLatin1().constData());
+#endif
+
+ if (postDevice) {
+ postDevice->seek(0); // reposition the device
+ bytesTotal += postDevice->size();
+ //check for 100-continue
+ if (header.value(QLatin1String("expect")).contains(QLatin1String("100-continue"), Qt::CaseInsensitive)) {
+ //create a time out for 2 secs.
+ pendingPost = true;
+ post100ContinueTimer.start(2000);
+ }
+ } else {
+ bytesTotal += buffer.size();
+ socket->write(buffer, buffer.size());
+ }
+}
+
+void QHttpPrivate::_q_slotError(QAbstractSocket::SocketError err)
+{
+ Q_Q(QHttp);
+ postDevice = 0;
+
+ if (state == QHttp::Connecting || state == QHttp::Reading || state == QHttp::Sending) {
+ switch (err) {
+ case QTcpSocket::ConnectionRefusedError:
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused (or timed out)")), QHttp::ConnectionRefused);
+ break;
+ case QTcpSocket::HostNotFoundError:
+ finishedWithError(QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
+ .arg(socket->peerName()), QHttp::HostNotFound);
+ break;
+ case QTcpSocket::RemoteHostClosedError:
+ if (state == QHttp::Sending && reconnectAttempts--) {
+ setState(QHttp::Closing);
+ setState(QHttp::Unconnected);
+ socket->blockSignals(true);
+ socket->abort();
+ socket->blockSignals(false);
+ QMetaObject::invokeMethod(q, "_q_slotSendRequest", Qt::QueuedConnection);
+ return;
+ }
+ break;
+#ifndef QT_NO_NETWORKPROXY
+ case QTcpSocket::ProxyAuthenticationRequiredError:
+ finishedWithError(socket->errorString(), QHttp::ProxyAuthenticationRequiredError);
+ break;
+#endif
+ default:
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTP request failed")), QHttp::UnknownError);
+ break;
+ }
+ }
+
+ closeConn();
+}
+
+#ifndef QT_NO_OPENSSL
+void QHttpPrivate::_q_slotEncryptedBytesWritten(qint64 written)
+{
+ Q_UNUSED(written);
+ postMoreData();
+}
+#endif
+
+void QHttpPrivate::_q_slotBytesWritten(qint64 written)
+{
+ Q_Q(QHttp);
+ bytesDone += written;
+ emit q->dataSendProgress(bytesDone, bytesTotal);
+ postMoreData();
+}
+
+// Send the POST data
+void QHttpPrivate::postMoreData()
+{
+ if (pendingPost)
+ return;
+
+ if (!postDevice)
+ return;
+
+ // the following is backported code from Qt 4.6 QNetworkAccessManager.
+ // We also have to check the encryptedBytesToWrite() if it is an SSL socket.
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+ // if it is really an ssl socket, check more than just bytesToWrite()
+ if ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) == 0) {
+#else
+ if (socket->bytesToWrite() == 0) {
+#endif
+ int max = qMin<qint64>(4096, postDevice->size() - postDevice->pos());
+ QByteArray arr;
+ arr.resize(max);
+
+ int n = postDevice->read(arr.data(), max);
+ if (n < 0) {
+ qWarning("Could not read enough bytes from the device");
+ closeConn();
+ return;
+ }
+ if (postDevice->atEnd()) {
+ postDevice = 0;
+ }
+
+ socket->write(arr, n);
+ }
+}
+
+void QHttpPrivate::_q_slotReadyRead()
+{
+ Q_Q(QHttp);
+ QHttp::State oldState = state;
+ if (state != QHttp::Reading) {
+ setState(QHttp::Reading);
+ readHeader = true;
+ headerStr = QLatin1String("");
+ bytesDone = 0;
+ chunkedSize = -1;
+ repost = false;
+ }
+
+ while (readHeader) {
+ bool end = false;
+ QString tmp;
+ while (!end && socket->canReadLine()) {
+ tmp = QString::fromAscii(socket->readLine());
+ if (tmp == QLatin1String("\r\n") || tmp == QLatin1String("\n") || tmp.isEmpty())
+ end = true;
+ else
+ headerStr += tmp;
+ }
+
+ if (!end)
+ return;
+
+ response = QHttpResponseHeader(headerStr);
+ headerStr = QLatin1String("");
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp: read response header:\n---{\n%s}---", response.toString().toLatin1().constData());
+#endif
+ // Check header
+ if (!response.isValid()) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP response header")),
+ QHttp::InvalidResponseHeader);
+ closeConn();
+ return;
+ }
+
+ int statusCode = response.statusCode();
+ if (statusCode == 401 || statusCode == 407) { // (Proxy) Authentication required
+ QAuthenticator *auth =
+#ifndef QT_NO_NETWORKPROXY
+ statusCode == 407
+ ? &proxyAuthenticator :
+#endif
+ &authenticator;
+ if (auth->isNull())
+ auth->detach();
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
+ priv->parseHttpResponse(response, (statusCode == 407));
+ if (priv->phase == QAuthenticatorPrivate::Done) {
+ socket->blockSignals(true);
+#ifndef QT_NO_NETWORKPROXY
+ if (statusCode == 407)
+ emit q->proxyAuthenticationRequired(proxy, auth);
+ else
+#endif
+ emit q->authenticationRequired(hostName, port, auth);
+ socket->blockSignals(false);
+ } else if (priv->phase == QAuthenticatorPrivate::Invalid) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown authentication method")),
+ QHttp::AuthenticationRequiredError);
+ closeConn();
+ return;
+ }
+
+ // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
+ if (priv->phase == QAuthenticatorPrivate::Done) {
+#ifndef QT_NO_NETWORKPROXY
+ if (statusCode == 407)
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy authentication required")),
+ QHttp::ProxyAuthenticationRequiredError);
+ else
+#endif
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Authentication required")),
+ QHttp::AuthenticationRequiredError);
+ closeConn();
+ return;
+ } else {
+ // close the connection if it isn't already and reconnect using the chosen authentication method
+ bool willClose = (response.value(QLatin1String("proxy-connection")).toLower() == QLatin1String("close"))
+ || (response.value(QLatin1String("connection")).toLower() == QLatin1String("close"));
+ if (willClose) {
+ if (socket) {
+ setState(QHttp::Closing);
+ socket->blockSignals(true);
+ socket->close();
+ socket->blockSignals(false);
+ socket->readAll();
+ }
+ _q_slotSendRequest();
+ return;
+ } else {
+ repost = true;
+ }
+ }
+ } else {
+ buffer.clear();
+ }
+
+ if (response.statusCode() == 100 && pendingPost) {
+ // if we have pending POST, start sending data otherwise ignore
+ post100ContinueTimer.stop();
+ QMetaObject::invokeMethod(q, "_q_continuePost", Qt::QueuedConnection);
+ return;
+ }
+
+ // The 100-continue header is ignored (in case of no 'expect:100-continue' header),
+ // because when using the POST method, we send both the request header and data in
+ // one chunk.
+ if (response.statusCode() != 100) {
+ post100ContinueTimer.stop();
+ pendingPost = false;
+ readHeader = false;
+ if (response.hasKey(QLatin1String("transfer-encoding")) &&
+ response.value(QLatin1String("transfer-encoding")).toLower().contains(QLatin1String("chunked")))
+ chunkedSize = 0;
+
+ if (!repost)
+ emit q->responseHeaderReceived(response);
+ if (state == QHttp::Unconnected || state == QHttp::Closing)
+ return;
+ } else {
+ // Restore the state, the next incoming data will be treated as if
+ // we never say the 100 response.
+ state = oldState;
+ }
+ }
+
+ bool everythingRead = false;
+
+ if (q->currentRequest().method() == QLatin1String("HEAD") ||
+ response.statusCode() == 304 || response.statusCode() == 204 ||
+ response.statusCode() == 205) {
+ // HEAD requests have only headers as replies
+ // These status codes never have a body:
+ // 304 Not Modified
+ // 204 No Content
+ // 205 Reset Content
+ everythingRead = true;
+ } else {
+ qint64 n = socket->bytesAvailable();
+ QByteArray *arr = 0;
+ if (chunkedSize != -1) {
+ // transfer-encoding is chunked
+ for (;;) {
+ // get chunk size
+ if (chunkedSize == 0) {
+ if (!socket->canReadLine())
+ break;
+ QString sizeString = QString::fromAscii(socket->readLine());
+ int tPos = sizeString.indexOf(QLatin1Char(';'));
+ if (tPos != -1)
+ sizeString.truncate(tPos);
+ bool ok;
+ chunkedSize = sizeString.toInt(&ok, 16);
+ if (!ok) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP chunked body")),
+ QHttp::WrongContentLength);
+ closeConn();
+ delete arr;
+ return;
+ }
+ if (chunkedSize == 0) // last-chunk
+ chunkedSize = -2;
+ }
+
+ // read trailer
+ while (chunkedSize == -2 && socket->canReadLine()) {
+ QString read = QString::fromAscii(socket->readLine());
+ if (read == QLatin1String("\r\n") || read == QLatin1String("\n"))
+ chunkedSize = -1;
+ }
+ if (chunkedSize == -1) {
+ everythingRead = true;
+ break;
+ }
+
+ // make sure that you can read the terminating CRLF,
+ // otherwise wait until next time...
+ n = socket->bytesAvailable();
+ if (n == 0)
+ break;
+ if (n == chunkedSize || n == chunkedSize+1) {
+ n = chunkedSize - 1;
+ if (n == 0)
+ break;
+ }
+
+ // read data
+ qint64 toRead = chunkedSize < 0 ? n : qMin(n, chunkedSize);
+ if (!arr)
+ arr = new QByteArray;
+ uint oldArrSize = arr->size();
+ arr->resize(oldArrSize + toRead);
+ qint64 read = socket->read(arr->data()+oldArrSize, toRead);
+ arr->resize(oldArrSize + read);
+
+ chunkedSize -= read;
+
+ if (chunkedSize == 0 && n - read >= 2) {
+ // read terminating CRLF
+ char tmp[2];
+ socket->read(tmp, 2);
+ if (tmp[0] != '\r' || tmp[1] != '\n') {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP chunked body")),
+ QHttp::WrongContentLength);
+ closeConn();
+ delete arr;
+ return;
+ }
+ }
+ }
+ } else if (response.hasContentLength()) {
+ if (repost && (n < response.contentLength())) {
+ // wait for the content to be available fully
+ // if repost is required, the content is ignored
+ return;
+ }
+ n = qMin(qint64(response.contentLength() - bytesDone), n);
+ if (n > 0) {
+ arr = new QByteArray;
+ arr->resize(n);
+ qint64 read = socket->read(arr->data(), n);
+ arr->resize(read);
+ }
+ if (bytesDone + q->bytesAvailable() + n == response.contentLength())
+ everythingRead = true;
+ } else if (n > 0) {
+ // workaround for VC++ bug
+ QByteArray temp = socket->readAll();
+ arr = new QByteArray(temp);
+ }
+
+ if (arr && !repost) {
+ n = arr->size();
+ if (toDevice) {
+ qint64 bytesWritten;
+ bytesWritten = toDevice->write(*arr, n);
+ delete arr;
+ arr = 0;
+ // if writing to the device does not succeed, quit with error
+ if (bytesWritten == -1 || bytesWritten < n) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Error writing response to device")), QHttp::UnknownError);
+ } else {
+ bytesDone += bytesWritten;
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::_q_slotReadyRead(): read %lld bytes (%lld bytes done)", n, bytesDone);
+#endif
+ }
+ if (response.hasContentLength())
+ emit q->dataReadProgress(bytesDone, response.contentLength());
+ else
+ emit q->dataReadProgress(bytesDone, 0);
+ } else {
+ char *ptr = rba.reserve(arr->size());
+ memcpy(ptr, arr->data(), arr->size());
+ delete arr;
+ arr = 0;
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::_q_slotReadyRead(): read %lld bytes (%lld bytes done)", n, bytesDone + q->bytesAvailable());
+#endif
+ if (response.hasContentLength())
+ emit q->dataReadProgress(bytesDone + q->bytesAvailable(), response.contentLength());
+ else
+ emit q->dataReadProgress(bytesDone + q->bytesAvailable(), 0);
+ emit q->readyRead(response);
+ }
+ }
+
+ delete arr;
+ }
+
+ if (everythingRead) {
+ if (repost) {
+ _q_slotSendRequest();
+ return;
+ }
+ // Handle "Connection: close"
+ if (response.value(QLatin1String("connection")).toLower() == QLatin1String("close")) {
+ closeConn();
+ } else {
+ setState(QHttp::Connected);
+ // Start a timer, so that we emit the keep alive signal
+ // "after" this method returned.
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+ }
+ }
+}
+
+void QHttpPrivate::_q_slotDoFinished()
+{
+ if (state == QHttp::Connected) {
+ finishedWithSuccess();
+ } else if (state != QHttp::Unconnected) {
+ setState(QHttp::Unconnected);
+ finishedWithSuccess();
+ }
+}
+
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+QHttp::State QHttp::state() const
+{
+ Q_D(const QHttp);
+ return d->state;
+}
+
+/*!
+ Returns the last error that occurred. This is useful to find out
+ what happened when receiving a requestFinished() or a done()
+ signal with the \c error argument \c true.
+
+ If you start a new request, the error status is reset to \c NoError.
+*/
+QHttp::Error QHttp::error() const
+{
+ Q_D(const QHttp);
+ return d->error;
+}
+
+/*!
+ Returns a human-readable description of the last error that
+ occurred. This is useful to present a error message to the user
+ when receiving a requestFinished() or a done() signal with the \c
+ error argument \c true.
+*/
+QString QHttp::errorString() const
+{
+ Q_D(const QHttp);
+ return d->errorString;
+}
+
+void QHttpPrivate::setState(int s)
+{
+ Q_Q(QHttp);
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp state changed %d -> %d", state, s);
+#endif
+ state = QHttp::State(s);
+ emit q->stateChanged(s);
+}
+
+void QHttpPrivate::closeConn()
+{
+ Q_Q(QHttp);
+ // If no connection is open -> ignore
+ if (state == QHttp::Closing || state == QHttp::Unconnected)
+ return;
+
+ postDevice = 0;
+ setState(QHttp::Closing);
+
+ // Already closed ?
+ if (!socket || !socket->isOpen()) {
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+ } else {
+ // Close now.
+ socket->close();
+ }
+}
+
+void QHttpPrivate::setSock(QTcpSocket *sock)
+{
+ Q_Q(const QHttp);
+
+ // disconnect all existing signals
+ if (socket)
+ socket->disconnect();
+ if (deleteSocket)
+ delete socket;
+
+ // use the new QTcpSocket socket, or create one if socket is 0.
+ deleteSocket = (sock == 0);
+ socket = sock;
+ if (!socket) {
+#ifndef QT_NO_OPENSSL
+ if (QSslSocket::supportsSsl())
+ socket = new QSslSocket();
+ else
+#endif
+ socket = new QTcpSocket();
+ }
+
+ // connect all signals
+ QObject::connect(socket, SIGNAL(connected()), q, SLOT(_q_slotConnected()));
+ QObject::connect(socket, SIGNAL(disconnected()), q, SLOT(_q_slotClosed()));
+ QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(_q_slotReadyRead()));
+ QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), q, SLOT(_q_slotError(QAbstractSocket::SocketError)));
+ QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
+ q, SLOT(_q_slotBytesWritten(qint64)));
+#ifndef QT_NO_NETWORKPROXY
+ QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+#endif
+
+#ifndef QT_NO_OPENSSL
+ if (qobject_cast<QSslSocket *>(socket)) {
+ QObject::connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ q, SIGNAL(sslErrors(QList<QSslError>)));
+ QObject::connect(socket, SIGNAL(encryptedBytesWritten(qint64)),
+ q, SLOT(_q_slotEncryptedBytesWritten(qint64)));
+ }
+#endif
+}
+
+/*!
+ Tells the QSslSocket used for the Http connection to ignore the errors
+ reported in the sslErrors() signal.
+
+ Note that this function must be called from within a slot connected to the
+ sslErrors() signal to have any effect.
+
+ \sa QSslSocket QSslSocket::sslErrors()
+*/
+#ifndef QT_NO_OPENSSL
+void QHttp::ignoreSslErrors()
+{
+ Q_D(QHttp);
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(d->socket);
+ if (sslSocket)
+ sslSocket->ignoreSslErrors();
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qhttp.cpp"
+
+#endif
diff --git a/src/network/access/qhttp.h b/src/network/access/qhttp.h
new file mode 100644
index 0000000000..d6156f5941
--- /dev/null
+++ b/src/network/access/qhttp.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTP_H
+#define QHTTP_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_HTTP
+
+class QTcpSocket;
+class QTimerEvent;
+class QIODevice;
+class QAuthenticator;
+class QNetworkProxy;
+class QSslError;
+
+class QHttpPrivate;
+
+class QHttpHeaderPrivate;
+class Q_NETWORK_EXPORT QHttpHeader
+{
+public:
+ QHttpHeader();
+ QHttpHeader(const QHttpHeader &header);
+ QHttpHeader(const QString &str);
+ virtual ~QHttpHeader();
+
+ QHttpHeader &operator=(const QHttpHeader &h);
+
+ void setValue(const QString &key, const QString &value);
+ void setValues(const QList<QPair<QString, QString> > &values);
+ void addValue(const QString &key, const QString &value);
+ QList<QPair<QString, QString> > values() const;
+ bool hasKey(const QString &key) const;
+ QStringList keys() const;
+ QString value(const QString &key) const;
+ QStringList allValues(const QString &key) const;
+ void removeValue(const QString &key);
+ void removeAllValues(const QString &key);
+
+ // ### Qt 5: change to qint64
+ bool hasContentLength() const;
+ uint contentLength() const;
+ void setContentLength(int len);
+
+ bool hasContentType() const;
+ QString contentType() const;
+ void setContentType(const QString &type);
+
+ virtual QString toString() const;
+ bool isValid() const;
+
+ virtual int majorVersion() const = 0;
+ virtual int minorVersion() const = 0;
+
+protected:
+ virtual bool parseLine(const QString &line, int number);
+ bool parse(const QString &str);
+ void setValid(bool);
+
+ QHttpHeader(QHttpHeaderPrivate &dd, const QString &str = QString());
+ QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header);
+ QScopedPointer<QHttpHeaderPrivate> d_ptr;
+
+private:
+ Q_DECLARE_PRIVATE(QHttpHeader)
+};
+
+class QHttpResponseHeaderPrivate;
+class Q_NETWORK_EXPORT QHttpResponseHeader : public QHttpHeader
+{
+public:
+ QHttpResponseHeader();
+ QHttpResponseHeader(const QHttpResponseHeader &header);
+ QHttpResponseHeader(const QString &str);
+ QHttpResponseHeader(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
+ QHttpResponseHeader &operator=(const QHttpResponseHeader &header);
+
+ void setStatusLine(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
+
+ int statusCode() const;
+ QString reasonPhrase() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine(const QString &line, int number);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpResponseHeader)
+ friend class QHttpPrivate;
+};
+
+class QHttpRequestHeaderPrivate;
+class Q_NETWORK_EXPORT QHttpRequestHeader : public QHttpHeader
+{
+public:
+ QHttpRequestHeader();
+ QHttpRequestHeader(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
+ QHttpRequestHeader(const QHttpRequestHeader &header);
+ QHttpRequestHeader(const QString &str);
+ QHttpRequestHeader &operator=(const QHttpRequestHeader &header);
+
+ void setRequest(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
+
+ QString method() const;
+ QString path() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine(const QString &line, int number);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpRequestHeader)
+};
+
+class Q_NETWORK_EXPORT QHttp : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ConnectionMode {
+ ConnectionModeHttp,
+ ConnectionModeHttps
+ };
+
+ explicit QHttp(QObject *parent = 0);
+ QHttp(const QString &hostname, quint16 port = 80, QObject *parent = 0);
+ QHttp(const QString &hostname, ConnectionMode mode, quint16 port = 0, QObject *parent = 0);
+ virtual ~QHttp();
+
+ enum State {
+ Unconnected,
+ HostLookup,
+ Connecting,
+ Sending,
+ Reading,
+ Connected,
+ Closing
+ };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ UnexpectedClose,
+ InvalidResponseHeader,
+ WrongContentLength,
+ Aborted,
+ AuthenticationRequiredError,
+ ProxyAuthenticationRequiredError
+ };
+
+ int setHost(const QString &hostname, quint16 port = 80);
+ int setHost(const QString &hostname, ConnectionMode mode, quint16 port = 0);
+
+ int setSocket(QTcpSocket *socket);
+ int setUser(const QString &username, const QString &password = QString());
+
+#ifndef QT_NO_NETWORKPROXY
+ int setProxy(const QString &host, int port,
+ const QString &username = QString(),
+ const QString &password = QString());
+ int setProxy(const QNetworkProxy &proxy);
+#endif
+
+ int get(const QString &path, QIODevice *to=0);
+ int post(const QString &path, QIODevice *data, QIODevice *to=0 );
+ int post(const QString &path, const QByteArray &data, QIODevice *to=0);
+ int head(const QString &path);
+ int request(const QHttpRequestHeader &header, QIODevice *device=0, QIODevice *to=0);
+ int request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to=0);
+
+ int closeConnection();
+ int close();
+
+ qint64 bytesAvailable() const;
+ qint64 read(char *data, qint64 maxlen);
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT qint64 readBlock(char *data, quint64 maxlen)
+ { return read(data, qint64(maxlen)); }
+#endif
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice *currentSourceDevice() const;
+ QIODevice *currentDestinationDevice() const;
+ QHttpRequestHeader currentRequest() const;
+ QHttpResponseHeader lastResponse() const;
+ bool hasPendingRequests() const;
+ void clearPendingRequests();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+#ifndef QT_NO_OPENSSL
+ void ignoreSslErrors();
+#endif
+
+Q_SIGNALS:
+ void stateChanged(int);
+ void responseHeaderReceived(const QHttpResponseHeader &resp);
+ void readyRead(const QHttpResponseHeader &resp);
+
+ // ### Qt 5: change to qint64
+ void dataSendProgress(int, int);
+ void dataReadProgress(int, int);
+
+ void requestStarted(int);
+ void requestFinished(int, bool);
+ void done(bool);
+
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *);
+#endif
+ void authenticationRequired(const QString &hostname, quint16 port, QAuthenticator *);
+
+#ifndef QT_NO_OPENSSL
+ void sslErrors(const QList<QSslError> &errors);
+#endif
+
+private:
+ Q_DISABLE_COPY(QHttp)
+ Q_DECLARE_PRIVATE(QHttp)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotReadyRead())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotConnected())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotError(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_slotClosed())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotBytesWritten(qint64 numBytes))
+#ifndef QT_NO_OPENSSL
+ Q_PRIVATE_SLOT(d_func(), void _q_slotEncryptedBytesWritten(qint64 numBytes))
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_slotDoFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotSendRequest())
+ Q_PRIVATE_SLOT(d_func(), void _q_continuePost())
+
+ friend class QHttpNormalRequest;
+ friend class QHttpSetHostRequest;
+ friend class QHttpSetSocketRequest;
+ friend class QHttpSetUserRequest;
+ friend class QHttpSetProxyRequest;
+ friend class QHttpCloseRequest;
+ friend class QHttpPGHRequest;
+};
+
+#endif // QT_NO_HTTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QHTTP_H
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
new file mode 100644
index 0000000000..640f9ead01
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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
+ 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 0000000000..0a3342c6bb
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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 0000000000..7dc13e9a61
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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
new file mode 100644
index 0000000000..83156c6a35
--- /dev/null
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -0,0 +1,1010 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qabstractsocket_p.h>
+#include "qhttpnetworkconnection_p.h"
+#include "qhttpnetworkconnectionchannel_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+#include <private/qnetworkrequest_p.h>
+#include <private/qobject_p.h>
+#include <private/qauthenticator_p.h>
+#include <qnetworkproxy.h>
+#include <qauthenticator.h>
+
+#include <qbuffer.h>
+#include <qpair.h>
+#include <qhttp.h>
+#include <qdebug.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <private/qsslsocket_p.h>
+# include <QtNetwork/qsslkey.h>
+# include <QtNetwork/qsslcipher.h>
+# include <QtNetwork/qsslconfiguration.h>
+#endif
+
+
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_SYMBIAN
+const int QHttpNetworkConnectionPrivate::defaultChannelCount = 3;
+#else
+const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
+#endif
+
+// The pipeline length. So there will be 4 requests in flight.
+const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
+// Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
+// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
+const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
+
+
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
+: state(RunningState),
+ hostName(hostName), port(port), encrypt(encrypt),
+ channelCount(defaultChannelCount)
+#ifndef QT_NO_NETWORKPROXY
+ , networkProxy(QNetworkProxy::NoProxy)
+#endif
+{
+ channels = new QHttpNetworkConnectionChannel[channelCount];
+}
+
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
+: state(RunningState),
+ hostName(hostName), port(port), encrypt(encrypt),
+ channelCount(channelCount)
+#ifndef QT_NO_NETWORKPROXY
+ , networkProxy(QNetworkProxy::NoProxy)
+#endif
+{
+ channels = new QHttpNetworkConnectionChannel[channelCount];
+}
+
+
+
+QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
+{
+ for (int i = 0; i < channelCount; ++i) {
+ if (channels[i].socket) {
+ channels[i].socket->close();
+ delete channels[i].socket;
+ }
+ }
+ delete []channels;
+}
+
+void QHttpNetworkConnectionPrivate::init()
+{
+ Q_Q(QHttpNetworkConnection);
+ 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();
+ }
+}
+
+void QHttpNetworkConnectionPrivate::pauseConnection()
+{
+ state = PausedState;
+
+ // Disable all socket notifiers
+ for (int i = 0; i < channelCount; i++) {
+#ifndef QT_NO_OPENSSL
+ if (encrypt)
+ QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
+ else
+#endif
+ QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
+ }
+}
+
+void QHttpNetworkConnectionPrivate::resumeConnection()
+{
+ state = RunningState;
+ // Enable all socket notifiers
+ for (int i = 0; i < channelCount; i++) {
+#ifndef QT_NO_OPENSSL
+ if (encrypt)
+ QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
+ else
+#endif
+ QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
+
+ // Resume pending upload if needed
+ if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
+ QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
+ }
+
+ // queue _q_startNextRequest
+ QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
+}
+
+int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
+{
+ for (int i = 0; i < channelCount; ++i)
+ if (channels[i].socket == socket)
+ return i;
+
+ qFatal("Called with unknown socket object.");
+ return 0;
+}
+
+qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
+{
+ return reply.d_func()->responseData.byteAmount();
+}
+
+qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
+{
+ return reply.d_func()->responseData.sizeNextBlock();
+}
+
+void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
+{
+ QHttpNetworkRequest &request = messagePair.first;
+ QHttpNetworkReply *reply = messagePair.second;
+
+ // add missing fields for the request
+ QByteArray value;
+ // check if Content-Length is provided
+ QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
+ if (uploadByteDevice) {
+ if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
+ // both values known, take the smaller one.
+ request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
+ } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
+ // content length not supplied by user, but the upload device knows it
+ request.setContentLength(uploadByteDevice->size());
+ } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
+ // everything OK, the user supplied us the contentLength
+ } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
+ qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
+ }
+ }
+ // set the Connection/Proxy-Connection: Keep-Alive headers
+#ifndef QT_NO_NETWORKPROXY
+ if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) {
+ value = request.headerField("proxy-connection");
+ if (value.isEmpty())
+ request.setHeaderField("Proxy-Connection", "Keep-Alive");
+ } else {
+#endif
+ value = request.headerField("connection");
+ if (value.isEmpty())
+ request.setHeaderField("Connection", "Keep-Alive");
+#ifndef QT_NO_NETWORKPROXY
+ }
+#endif
+
+ // If the request had a accept-encoding set, we better not mess
+ // with it. If it was not set, we announce that we understand gzip
+ // and remember this fact in request.d->autoDecompress so that
+ // we can later decompress the HTTP reply if it has such an
+ // encoding.
+ value = request.headerField("accept-encoding");
+ if (value.isEmpty()) {
+#ifndef QT_NO_COMPRESS
+ request.setHeaderField("Accept-Encoding", "gzip");
+ request.d->autoDecompress = true;
+#else
+ // if zlib is not available set this to false always
+ request.d->autoDecompress = false;
+#endif
+ }
+
+ // some websites mandate an accept-language header and fail
+ // if it is not sent. This is a problem with the website and
+ // not with us, but we work around this by setting
+ // one always.
+ value = request.headerField("accept-language");
+ if (value.isEmpty()) {
+ QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-'));
+ QString acceptLanguage;
+ if (systemLocale == QLatin1String("C"))
+ acceptLanguage = QString::fromAscii("en,*");
+ else if (systemLocale.startsWith(QLatin1String("en-")))
+ acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale);
+ else
+ acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale);
+ request.setHeaderField("Accept-Language", acceptLanguage.toAscii());
+ }
+
+ // set the User Agent
+ value = request.headerField("user-agent");
+ if (value.isEmpty())
+ request.setHeaderField("User-Agent", "Mozilla/5.0");
+ // set the host
+ value = request.headerField("host");
+ if (value.isEmpty()) {
+ QByteArray host = QUrl::toAce(hostName);
+
+ int port = request.url().port();
+ if (port != -1) {
+ host += ':';
+ host += QByteArray::number(port);
+ }
+
+ request.setHeaderField("Host", host);
+ }
+
+ reply->d_func()->requestIsPrepared = true;
+}
+
+
+
+
+void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
+ QHttpNetworkReply *reply,
+ QNetworkReply::NetworkError errorCode)
+{
+ Q_Q(QHttpNetworkConnection);
+ if (socket && reply) {
+ // this error matters only to this reply
+ reply->d_func()->errorString = errorDetail(errorCode, socket);
+ emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
+ 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);
+ }
+}
+
+void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
+{
+ Q_ASSERT(auth);
+
+ // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
+ if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
+ return;
+ if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
+ return;
+
+
+ // select another channel
+ QAuthenticator* otherAuth = 0;
+ for (int i = 0; i < channelCount; ++i) {
+ if (i == fromChannel)
+ continue;
+ if (isProxy)
+ otherAuth = &channels[i].proxyAuthenticator;
+ else
+ otherAuth = &channels[i].authenticator;
+ // if the credentials are different, copy them
+ if (otherAuth->user().compare(auth->user()))
+ otherAuth->setUser(auth->user());
+ if (otherAuth->password().compare(auth->password()))
+ otherAuth->setPassword(auth->password());
+ }
+}
+
+
+// handles the authentication for one channel and eventually re-starts the other channels
+bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
+ bool isProxy, bool &resend)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(reply);
+
+ resend = false;
+ //create the response header to be used with QAuthenticatorPrivate.
+ QList<QPair<QByteArray, QByteArray> > fields = reply->header();
+
+ //find out the type of authentication protocol requested.
+ QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
+ if (authMethod != QAuthenticatorPrivate::None) {
+ int i = indexOf(socket);
+ //Use a single authenticator for all domains. ### change later to use domain/realm
+ QAuthenticator* auth = 0;
+ if (isProxy) {
+ auth = &channels[i].proxyAuthenticator;
+ channels[i].proxyAuthMethod = authMethod;
+ } else {
+ auth = &channels[i].authenticator;
+ channels[i].authMethod = authMethod;
+ }
+ //proceed with the authentication.
+ if (auth->isNull())
+ auth->detach();
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
+ priv->parseHttpResponse(fields, isProxy);
+
+ if (priv->phase == QAuthenticatorPrivate::Done) {
+ pauseConnection();
+ if (!isProxy) {
+ emit reply->authenticationRequired(reply->request(), auth);
+#ifndef QT_NO_NETWORKPROXY
+ } else {
+ emit reply->proxyAuthenticationRequired(networkProxy, auth);
+#endif
+ }
+ resumeConnection();
+
+ if (priv->phase != QAuthenticatorPrivate::Done) {
+ // send any pending requests
+ copyCredentials(i, auth, isProxy);
+ }
+ }
+ // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
+ // then nothing was filled in by the user or the cache
+ // - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then
+ // we need to bail out if authentication is required.
+ if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
+ // Reset authenticator so the next request on that channel does not get messed up
+ auth = 0;
+ if (isProxy)
+ channels[i].proxyAuthenticator = QAuthenticator();
+ else
+ channels[i].authenticator = QAuthenticator();
+
+ // authentication is cancelled, send the current contents to the user.
+ emit channels[i].reply->headerChanged();
+ emit channels[i].reply->readyRead();
+ QNetworkReply::NetworkError errorCode =
+ isProxy
+ ? QNetworkReply::ProxyAuthenticationRequiredError
+ : QNetworkReply::AuthenticationRequiredError;
+ reply->d_func()->errorString = errorDetail(errorCode, socket);
+ emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
+ // ### at this point the reply could be deleted
+ socket->close();
+ return true;
+ }
+ //resend the request
+ resend = true;
+ return true;
+ }
+ return false;
+}
+
+void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
+{
+ Q_ASSERT(socket);
+
+ int i = indexOf(socket);
+
+ // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
+ if (channels[i].authMethod != QAuthenticatorPrivate::None) {
+ if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
+ request.setHeaderField("Authorization", response);
+ }
+ }
+ }
+
+ // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
+ if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
+ if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
+ request.setHeaderField("Proxy-Authorization", response);
+ }
+ }
+ }
+}
+
+QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
+{
+ Q_Q(QHttpNetworkConnection);
+
+ // The reply component of the pair is created initially.
+ QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
+ reply->setRequest(request);
+ reply->d_func()->connection = q;
+ reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
+ HttpMessagePair pair = qMakePair(request, reply);
+
+ switch (request.priority()) {
+ case QHttpNetworkRequest::HighPriority:
+ highPriorityQueue.prepend(pair);
+ break;
+ case QHttpNetworkRequest::NormalPriority:
+ case QHttpNetworkRequest::LowPriority:
+ lowPriorityQueue.prepend(pair);
+ break;
+ }
+
+ // this used to be called via invokeMethod and a QueuedConnection
+ // It is the only place _q_startNextRequest is called directly without going
+ // through the event loop using a QueuedConnection.
+ // This is dangerous because of recursion that might occur when emitting
+ // signals as DirectConnection from this code path. Therefore all signal
+ // emissions that can come out from this code path need to
+ // be QueuedConnection.
+ // We are currently trying to fine-tune this.
+ _q_startNextRequest();
+
+
+ return reply;
+}
+
+void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
+{
+ Q_Q(QHttpNetworkConnection);
+
+ QHttpNetworkRequest request = pair.first;
+ switch (request.priority()) {
+ case QHttpNetworkRequest::HighPriority:
+ highPriorityQueue.prepend(pair);
+ break;
+ case QHttpNetworkRequest::NormalPriority:
+ case QHttpNetworkRequest::LowPriority:
+ lowPriorityQueue.prepend(pair);
+ break;
+ }
+
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+}
+
+bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
+{
+ Q_ASSERT(socket);
+
+ int i = indexOf(socket);
+
+ if (!highPriorityQueue.isEmpty()) {
+ // remove from queue before sendRequest! else we might pipeline the same request again
+ HttpMessagePair messagePair = highPriorityQueue.takeLast();
+ if (!messagePair.second->d_func()->requestIsPrepared)
+ prepareRequest(messagePair);
+ channels[i].request = messagePair.first;
+ channels[i].reply = messagePair.second;
+ return true;
+ }
+
+ if (!lowPriorityQueue.isEmpty()) {
+ // remove from queue before sendRequest! else we might pipeline the same request again
+ HttpMessagePair messagePair = lowPriorityQueue.takeLast();
+ if (!messagePair.second->d_func()->requestIsPrepared)
+ prepareRequest(messagePair);
+ channels[i].request = messagePair.first;
+ channels[i].reply = messagePair.second;
+ return true;
+ }
+ return false;
+}
+
+// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
+void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
+{
+ // return fast if there is nothing to pipeline
+ if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
+ return;
+
+ int i = indexOf(socket);
+
+ // return fast if there was no reply right now processed
+ if (channels[i].reply == 0)
+ return;
+
+ if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
+ return;
+ }
+
+ if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
+ return;
+
+ // the current request that is in must already support pipelining
+ if (!channels[i].request.isPipeliningAllowed())
+ return;
+
+ // the current request must be a idempotent (right now we only check GET)
+ if (channels[i].request.operation() != QHttpNetworkRequest::Get)
+ return;
+
+ // check if socket is connected
+ if (socket->state() != QAbstractSocket::ConnectedState)
+ return;
+
+ // check for resendCurrent
+ if (channels[i].resendCurrent)
+ return;
+
+ // we do not like authentication stuff
+ // ### make sure to be OK with this in later releases
+ if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty())
+ return;
+ if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty())
+ return;
+
+ // must be in ReadingState or WaitingState
+ if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
+ || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
+ return;
+
+ int lengthBefore;
+ while (!highPriorityQueue.isEmpty()) {
+ lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ fillPipeline(highPriorityQueue, channels[i]);
+
+ 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
+ }
+
+ while (!lowPriorityQueue.isEmpty()) {
+ lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ fillPipeline(lowPriorityQueue, channels[i]);
+
+ 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
+bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
+{
+ if (queue.isEmpty())
+ return true;
+
+ for (int i = queue.count() - 1; i >= 0; --i) {
+ HttpMessagePair messagePair = queue.at(i);
+ const QHttpNetworkRequest &request = messagePair.first;
+
+ // we currently do not support pipelining if HTTP authentication is used
+ if (!request.url().userInfo().isEmpty())
+ continue;
+
+ // take only GET requests
+ if (request.operation() != QHttpNetworkRequest::Get)
+ continue;
+
+ if (!request.isPipeliningAllowed())
+ continue;
+
+ // remove it from the queue
+ queue.takeAt(i);
+ // we modify the queue we iterate over here, but since we return from the function
+ // afterwards this is fine.
+
+ // actually send it
+ if (!messagePair.second->d_func()->requestIsPrepared)
+ prepareRequest(messagePair);
+ channel.pipelineInto(messagePair);
+
+ // return false because we processed something and need to process again
+ return false;
+ }
+
+ // return true, the queue has been processed and not changed
+ return true;
+}
+
+
+QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket,
+ const QString &extraDetail)
+{
+ Q_ASSERT(socket);
+
+ QString errorString;
+ switch (errorCode) {
+ case QNetworkReply::HostNotFoundError:
+ errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
+ .arg(socket->peerName());
+ break;
+ case QNetworkReply::ConnectionRefusedError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused"));
+ break;
+ case QNetworkReply::RemoteHostClosedError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection closed"));
+ break;
+ case QNetworkReply::TimeoutError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QAbstractSocket", "Socket operation timed out"));
+ break;
+ case QNetworkReply::ProxyAuthenticationRequiredError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy requires authentication"));
+ break;
+ case QNetworkReply::AuthenticationRequiredError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Host requires authentication"));
+ break;
+ case QNetworkReply::ProtocolFailure:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Data corrupted"));
+ break;
+ case QNetworkReply::ProtocolUnknownError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown protocol specified"));
+ break;
+ case QNetworkReply::SslHandshakeFailedError:
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "SSL handshake failed"));
+ break;
+ default:
+ // all other errors are treated as QNetworkReply::UnknownNetworkError
+ errorString = extraDetail;
+ break;
+ }
+ return errorString;
+}
+
+// this is called from the destructor of QHttpNetworkReply. It is called when
+// the reply was finished correctly or when it was aborted.
+void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
+{
+ Q_Q(QHttpNetworkConnection);
+
+ // check if the reply is currently being processed or it is pipelined in
+ for (int i = 0; i < channelCount; ++i) {
+ // is the reply associated the currently processing of this channel?
+ if (channels[i].reply == reply) {
+ channels[i].reply = 0;
+ channels[i].request = QHttpNetworkRequest();
+ channels[i].resendCurrent = false;
+
+ if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
+ // the reply had to be prematurely removed, e.g. it was not finished
+ // therefore we have to requeue the already pipelined requests.
+ channels[i].requeueCurrentlyPipelinedRequests();
+ }
+
+ // if HTTP mandates we should close
+ // or the reply is not finished yet, e.g. it was aborted
+ // we have to close that connection
+ if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
+ channels[i].close();
+
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+
+ // is the reply inside the pipeline of this channel already?
+ for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
+ if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
+ // Remove that HttpMessagePair
+ channels[i].alreadyPipelinedRequests.removeAt(j);
+
+ channels[i].requeueCurrentlyPipelinedRequests();
+
+ // Since some requests had already been pipelined, but we removed
+ // one and re-queued the others
+ // we must force a connection close after the request that is
+ // currently in processing has been finished.
+ if (channels[i].reply)
+ channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
+
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+ }
+ }
+ // remove from the high priority queue
+ if (!highPriorityQueue.isEmpty()) {
+ for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
+ HttpMessagePair messagePair = highPriorityQueue.at(j);
+ if (messagePair.second == reply) {
+ highPriorityQueue.removeAt(j);
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+ }
+ }
+ // remove from the low priority queue
+ if (!lowPriorityQueue.isEmpty()) {
+ for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
+ HttpMessagePair messagePair = lowPriorityQueue.at(j);
+ if (messagePair.second == reply) {
+ lowPriorityQueue.removeAt(j);
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
+ }
+ }
+ }
+}
+
+
+
+// This function must be called from the event loop. The only
+// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
+// although it is called _q_startNextRequest, it will actually start multiple requests when possible
+void QHttpNetworkConnectionPrivate::_q_startNextRequest()
+{
+ // If the QHttpNetworkConnection is currently paused then bail out immediately
+ if (state == PausedState)
+ return;
+
+ //resend the necessary ones.
+ for (int i = 0; i < channelCount; ++i) {
+ if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
+ channels[i].resendCurrent = false;
+ channels[i].state = QHttpNetworkConnectionChannel::IdleState;
+
+ // if this is not possible, error will be emitted and connection terminated
+ if (!channels[i].resetUploadData())
+ continue;
+ channels[i].sendRequest();
+ }
+ }
+
+ // dequeue new ones
+
+ // return fast if there is nothing to do
+ if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
+ return;
+ // 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) {
+ if (dequeueRequest(channels[i].socket))
+ channels[i].sendRequest();
+ }
+ }
+
+ // try to push more into all sockets
+ // ### FIXME we should move this to the beginning of the function
+ // as soon as QtWebkit is properly using the pipelining
+ // (e.g. not for XMLHttpRequest or the first page load)
+ // ### FIXME we should also divide the requests more even
+ // on the connected sockets
+ //tryToFillPipeline(socket);
+ // return fast if there is nothing to pipeline
+ if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
+ return;
+ 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--;
+ }
+ }
+}
+
+
+void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
+{
+ for (int i = 0 ; i < channelCount; ++i) {
+ if (channels[i].reply == reply) {
+ // emulate a readyRead() from the socket
+ QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
+ return;
+ }
+ }
+}
+
+#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)
+{
+ Q_D(QHttpNetworkConnection);
+ d->init();
+}
+
+QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
+{
+ Q_D(QHttpNetworkConnection);
+ d->init();
+}
+#endif
+
+QHttpNetworkConnection::~QHttpNetworkConnection()
+{
+}
+
+QString QHttpNetworkConnection::hostName() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->hostName;
+}
+
+quint16 QHttpNetworkConnection::port() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->port;
+}
+
+QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
+{
+ Q_D(QHttpNetworkConnection);
+ return d->queueRequest(request);
+}
+
+bool QHttpNetworkConnection::isSsl() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->encrypt;
+}
+
+QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
+{
+ return d_func()->channels;
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
+{
+ Q_D(QHttpNetworkConnection);
+ d->networkProxy = networkProxy;
+ // update the authenticator
+ if (!d->networkProxy.user().isEmpty()) {
+ for (int i = 0; i < d->channelCount; ++i) {
+ d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
+ d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
+ }
+ }
+}
+
+QNetworkProxy QHttpNetworkConnection::cacheProxy() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->networkProxy;
+}
+
+void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
+{
+ Q_D(QHttpNetworkConnection);
+ for (int i = 0; i < d->channelCount; ++i)
+ d->channels[i].socket->setProxy(networkProxy);
+}
+
+QNetworkProxy QHttpNetworkConnection::transparentProxy() const
+{
+ Q_D(const QHttpNetworkConnection);
+ return d->channels[0].socket->proxy();
+}
+#endif
+
+
+// SSL support below
+#ifndef QT_NO_OPENSSL
+void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
+{
+ Q_D(QHttpNetworkConnection);
+ if (!d->encrypt)
+ return;
+
+ // set the config on all channels
+ for (int i = 0; i < d->channelCount; ++i)
+ static_cast<QSslSocket *>(d->channels[i].socket)->setSslConfiguration(config);
+}
+
+void QHttpNetworkConnection::ignoreSslErrors(int channel)
+{
+ Q_D(QHttpNetworkConnection);
+ if (!d->encrypt)
+ return;
+
+ if (channel == -1) { // ignore for all channels
+ for (int i = 0; i < d->channelCount; ++i) {
+ static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
+ d->channels[i].ignoreAllSslErrors = true;
+ }
+
+ } else {
+ static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
+ d->channels[channel].ignoreAllSslErrors = true;
+ }
+}
+
+void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
+{
+ Q_D(QHttpNetworkConnection);
+ if (!d->encrypt)
+ return;
+
+ if (channel == -1) { // ignore for all channels
+ for (int i = 0; i < d->channelCount; ++i) {
+ static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
+ d->channels[i].ignoreSslErrorsList = errors;
+ }
+
+ } else {
+ static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
+ d->channels[channel].ignoreSslErrorsList = errors;
+ }
+}
+
+#endif //QT_NO_OPENSSL
+
+#ifndef QT_NO_NETWORKPROXY
+// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
+// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
+// e.g. it is for SOCKS proxies which require authentication.
+void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
+{
+ // Also pause the connection because socket notifiers may fire while an user
+ // dialog is displaying
+ pauseConnection();
+ emit chan->reply->proxyAuthenticationRequired(proxy, auth);
+ resumeConnection();
+ int i = indexOf(chan->socket);
+ copyCredentials(i, auth, true);
+}
+#endif
+
+
+QT_END_NAMESPACE
+
+#include "moc_qhttpnetworkconnection_p.cpp"
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
new file mode 100644
index 0000000000..adb779f473
--- /dev/null
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKCONNECTION_H
+#define QHTTPNETWORKCONNECTION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qnetworksession.h>
+
+#include <private/qobject_p.h>
+#include <qauthenticator.h>
+#include <qnetworkproxy.h>
+#include <qbuffer.h>
+
+#include <private/qhttpnetworkheader_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qhttpnetworkreply_p.h>
+
+#include <private/qhttpnetworkconnectionchannel_p.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslsocket.h>
+# include <QtNetwork/qsslerror.h>
+#else
+# include <QtNetwork/qtcpsocket.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkRequest;
+class QHttpNetworkReply;
+class QByteArray;
+
+class QHttpNetworkConnectionPrivate;
+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.
+ QString hostName() const;
+ //The HTTP port in use.
+ quint16 port() const;
+
+ //add a new HTTP request through this connection
+ QHttpNetworkReply* sendRequest(const QHttpNetworkRequest &request);
+
+#ifndef QT_NO_NETWORKPROXY
+ //set the proxy for this connection
+ void setCacheProxy(const QNetworkProxy &networkProxy);
+ QNetworkProxy cacheProxy() const;
+ void setTransparentProxy(const QNetworkProxy &networkProxy);
+ QNetworkProxy transparentProxy() const;
+#endif
+
+ bool isSsl() const;
+
+ QHttpNetworkConnectionChannel *channels() const;
+
+#ifndef QT_NO_OPENSSL
+ void setSslConfiguration(const QSslConfiguration &config);
+ void ignoreSslErrors(int channel = -1);
+ void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
+#endif
+
+private:
+ Q_DECLARE_PRIVATE(QHttpNetworkConnection)
+ Q_DISABLE_COPY(QHttpNetworkConnection)
+ friend class QHttpNetworkReply;
+ friend class QHttpNetworkReplyPrivate;
+ friend class QHttpNetworkConnectionChannel;
+
+ Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
+};
+
+
+// private classes
+typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
+
+
+class QHttpNetworkConnectionPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpNetworkConnection)
+public:
+ static const int defaultChannelCount;
+ static const int defaultPipelineLength;
+ static const int defaultRePipelineLength;
+
+ enum ConnectionState {
+ RunningState = 0,
+ PausedState = 1,
+ };
+
+ QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
+ QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt);
+ ~QHttpNetworkConnectionPrivate();
+ void init();
+
+ void pauseConnection();
+ void resumeConnection();
+ ConnectionState state;
+
+ enum { ChunkSize = 4096 };
+
+ int indexOf(QAbstractSocket *socket) const;
+
+ QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
+ void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
+ bool dequeueRequest(QAbstractSocket *socket);
+ void prepareRequest(HttpMessagePair &request);
+
+ void fillPipeline(QAbstractSocket *socket);
+ bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
+
+ // read more HTTP body after the next event loop spin
+ void readMoreLater(QHttpNetworkReply *reply);
+
+ void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
+
+ // private slots
+ void _q_startNextRequest(); // send the next request from the queue
+
+ void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
+
+ QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
+ const QString &extraDetail = QString());
+
+#ifndef QT_NO_COMPRESS
+ bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
+#endif
+ void removeReply(QHttpNetworkReply *reply);
+
+ QString hostName;
+ quint16 port;
+ bool encrypt;
+
+ const int channelCount;
+ QHttpNetworkConnectionChannel *channels; // parallel connections to the server
+
+ qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
+ qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
+
+
+ void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
+ bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy networkProxy;
+ void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth);
+#endif
+
+ //The request queues
+ QList<HttpMessagePair> highPriorityQueue;
+ QList<HttpMessagePair> lowPriorityQueue;
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
+
+ friend class QHttpNetworkConnectionChannel;
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
new file mode 100644
index 0000000000..6fbc6f8056
--- /dev/null
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -0,0 +1,1162 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkconnection_p.h"
+#include "qhttpnetworkconnectionchannel_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+
+#include <qpair.h>
+#include <qdebug.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslkey.h>
+# include <QtNetwork/qsslcipher.h>
+# 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)
+ , bytesTotal(0)
+ , resendCurrent(false)
+ , lastStatus(0)
+ , pendingEncrypt(false)
+ , reconnectAttempts(2)
+ , authMethod(QAuthenticatorPrivate::None)
+ , proxyAuthMethod(QAuthenticatorPrivate::None)
+#ifndef QT_NO_OPENSSL
+ , ignoreAllSslErrors(false)
+#endif
+ , pipeliningSupported(PipeliningSupportUnknown)
+ , connection(0)
+{
+ // Inlining this function in the header leads to compiler error on
+ // release-armv5, on at least timebox 9.2 and 10.1.
+}
+
+void QHttpNetworkConnectionChannel::init()
+{
+#ifndef QT_NO_OPENSSL
+ if (connection->d_func()->encrypt)
+ socket = new QSslSocket;
+ else
+ socket = new QTcpSocket;
+#else
+ socket = new QTcpSocket;
+#endif
+#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)),
+ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(connected()),
+ this, SLOT(_q_connected()),
+ Qt::DirectConnection);
+ QObject::connect(socket, SIGNAL(readyRead()),
+ this, SLOT(_q_readyRead()),
+ Qt::DirectConnection);
+
+ // The disconnected() and error() signals may already come
+ // while calling connectToHost().
+ // In case of a cached hostname or an IP this
+ // will then emit a signal to the user of QNetworkReply
+ // but cannot be caught because the user did not have a chance yet
+ // to connect to QNetworkReply's signals.
+ qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
+ QObject::connect(socket, SIGNAL(disconnected()),
+ this, SLOT(_q_disconnected()),
+ Qt::QueuedConnection);
+ QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(_q_error(QAbstractSocket::SocketError)),
+ Qt::QueuedConnection);
+
+
+#ifndef QT_NO_NETWORKPROXY
+ QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ Qt::DirectConnection);
+#endif
+
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+ if (sslSocket) {
+ // won't be a sslSocket if encrypt is false
+ QObject::connect(sslSocket, SIGNAL(encrypted()),
+ this, SLOT(_q_encrypted()),
+ Qt::DirectConnection);
+ QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(_q_sslErrors(QList<QSslError>)),
+ Qt::DirectConnection);
+ QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
+ this, SLOT(_q_encryptedBytesWritten(qint64)),
+ Qt::DirectConnection);
+ }
+#endif
+}
+
+
+void QHttpNetworkConnectionChannel::close()
+{
+ if (socket->state() == QAbstractSocket::UnconnectedState)
+ state = QHttpNetworkConnectionChannel::IdleState;
+ else
+ state = QHttpNetworkConnectionChannel::ClosingState;
+
+ socket->close();
+}
+
+
+bool QHttpNetworkConnectionChannel::sendRequest()
+{
+ if (!reply) {
+ // heh, how should that happen!
+ qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
+ state = QHttpNetworkConnectionChannel::IdleState;
+ return false;
+ }
+
+ switch (state) {
+ case QHttpNetworkConnectionChannel::IdleState: { // write the header
+ if (!ensureConnection()) {
+ // wait for the connection (and encryption) to be done
+ // sendRequest will be called again from either
+ // _q_connected or _q_encrypted
+ return false;
+ }
+ written = 0; // excluding the header
+ bytesTotal = 0;
+
+ 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
+ if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
+ QUrl url = request.url();
+ QAuthenticator &auth = authenticator;
+ if (url.userName() != auth.user()
+ || (!url.password().isEmpty() && url.password() != auth.password())) {
+ auth.setUser(url.userName());
+ auth.setPassword(url.password());
+ emit reply->cacheCredentials(request, &auth);
+ connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
+ }
+ // clear the userinfo, since we use the same request for resending
+ // userinfo in url can conflict with the one in the authenticator
+ url.setUserInfo(QString());
+ request.setUrl(url);
+ }
+ // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
+ // and withCredentials has not been set to true.
+ if (request.withCredentials())
+ connection->d_func()->createAuthorization(socket, request);
+#ifndef QT_NO_NETWORKPROXY
+ QByteArray header = QHttpNetworkRequestPrivate::header(request,
+ (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
+#else
+ QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
+#endif
+ socket->write(header);
+ // flushing is dangerous (QSslSocket calls transmit which might read or error)
+// socket->flush();
+ QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
+ if (uploadByteDevice) {
+ // connect the signals so this function gets called again
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
+
+ bytesTotal = request.contentLength();
+
+ state = QHttpNetworkConnectionChannel::WritingState; // start writing data
+ sendRequest(); //recurse
+ } else {
+ state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
+ sendRequest(); //recurse
+ }
+
+ break;
+ }
+ case QHttpNetworkConnectionChannel::WritingState:
+ {
+ // write the data
+ QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
+ if (!uploadByteDevice || bytesTotal == written) {
+ if (uploadByteDevice)
+ emit reply->dataSendProgress(written, bytesTotal);
+ state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
+ sendRequest(); // recurse
+ break;
+ }
+
+ // only feed the QTcpSocket buffer when there is less than 32 kB in it
+ const qint64 socketBufferFill = 32*1024;
+ const qint64 socketWriteMaxSize = 16*1024;
+
+
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+ // if it is really an ssl socket, check more than just bytesToWrite()
+ while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
+ <= socketBufferFill && bytesTotal != written)
+#else
+ while (socket->bytesToWrite() <= socketBufferFill
+ && bytesTotal != written)
+#endif
+ {
+ // get pointer to upload data
+ qint64 currentReadSize = 0;
+ qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
+ const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
+
+ if (currentReadSize == -1) {
+ // premature eof happened
+ connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ break;
+ } else if (readPointer == 0 || currentReadSize == 0) {
+ // nothing to read currently, break the loop
+ break;
+ } else {
+ qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
+ if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
+ // socket broke down
+ connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ } else {
+ written += currentWriteSize;
+ uploadByteDevice->advanceReadPointer(currentWriteSize);
+
+ emit reply->dataSendProgress(written, bytesTotal);
+
+ if (written == bytesTotal) {
+ // make sure this function is called once again
+ state = QHttpNetworkConnectionChannel::WaitingState;
+ sendRequest();
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case QHttpNetworkConnectionChannel::WaitingState:
+ {
+ QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
+ if (uploadByteDevice) {
+ QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
+ }
+
+ // HTTP pipelining
+ //connection->d_func()->fillPipeline(socket);
+ //socket->flush();
+
+ // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
+ // this is needed if the sends an reply before we have finished sending the request. In that
+ // case receiveReply had been called before but ignored the server reply
+ if (socket->bytesAvailable())
+ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
+ break;
+ }
+ case QHttpNetworkConnectionChannel::ReadingState:
+ // ignore _q_bytesWritten in these states
+ // fall through
+ default:
+ break;
+ }
+ return true;
+}
+
+
+void QHttpNetworkConnectionChannel::_q_receiveReply()
+{
+ Q_ASSERT(socket);
+
+ if (!reply) {
+ // heh, how should that happen!
+ qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
+ << socket->bytesAvailable() << "bytes on socket.";
+ close();
+ return;
+ }
+
+ // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
+ // this function is called from _q_disconnected which is called because
+ // of ~QHttpNetworkConnectionPrivate
+ if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
+ return;
+ }
+
+ QAbstractSocket::SocketState socketState = socket->state();
+
+ // connection might be closed to signal the end of data
+ if (socketState == QAbstractSocket::UnconnectedState) {
+ if (socket->bytesAvailable() <= 0) {
+ if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
+ // finish this reply. this case happens when the server did not send a content length
+ reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
+ allDone();
+ return;
+ } else {
+ handleUnexpectedEOF();
+ return;
+ }
+ } else {
+ // socket not connected but still bytes for reading.. just continue in this function
+ }
+ }
+
+ // read loop for the response
+ qint64 bytes = 0;
+ qint64 lastBytes = bytes;
+ do {
+ lastBytes = bytes;
+
+ QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
+ switch (state) {
+ case QHttpNetworkReplyPrivate::NothingDoneState: {
+ state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
+ // fallthrough
+ }
+ case QHttpNetworkReplyPrivate::ReadingStatusState: {
+ qint64 statusBytes = reply->d_func()->readStatus(socket);
+ if (statusBytes == -1) {
+ // connection broke while reading status. also handled if later _q_disconnected is called
+ handleUnexpectedEOF();
+ return;
+ }
+ bytes += statusBytes;
+ lastStatus = reply->d_func()->statusCode;
+ break;
+ }
+ case QHttpNetworkReplyPrivate::ReadingHeaderState: {
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ qint64 headerBytes = replyPrivate->readHeader(socket);
+ if (headerBytes == -1) {
+ // connection broke while reading headers. also handled if later _q_disconnected is called
+ handleUnexpectedEOF();
+ 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
+ replyPrivate->removeAutoDecompressHeader();
+ } else {
+ replyPrivate->autoDecompress = false;
+ }
+ if (replyPrivate->statusCode == 100) {
+ replyPrivate->clearHttpLayerInformation();
+ replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
+ break; // ignore
+ }
+ 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();
+ break;
+ }
+ }
+ break;
+ }
+ case QHttpNetworkReplyPrivate::ReadingDataState: {
+ QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
+ if (socket->state() == QAbstractSocket::ConnectedState &&
+ replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
+ // (only do the following when still connected, not when we have already been disconnected and there is still data)
+ // We already have some HTTP body data. We don't read more from the socket until
+ // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
+ // we could not limit our read buffer usage.
+ // We only do this when shouldEmitSignals==true because our HTTP parsing
+ // always needs to parse the 401/407 replies. Therefore they don't really obey
+ // to the read buffer maximum size, but we don't care since they should be small.
+ return;
+ }
+
+ 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()) {
+ emit reply->readyRead();
+ emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ }
+ else
+ {
+ // use the traditional slower reading (for compressed encoding, chunked encoding,
+ // no content-length etc)
+ QByteDataBuffer byteDatas;
+ qint64 haveRead = replyPrivate->readBody(socket, &byteDatas);
+ if (haveRead) {
+ bytes += haveRead;
+ if (replyPrivate->autoDecompress)
+ replyPrivate->appendCompressedReplyData(byteDatas);
+ else
+ replyPrivate->appendUncompressedReplyData(byteDatas);
+
+ if (!replyPrivate->autoDecompress) {
+ replyPrivate->totalProgress += bytes;
+ if (replyPrivate->shouldEmitSignals()) {
+ // 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();
+ emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ }
+#ifndef QT_NO_COMPRESS
+ else if (!expand(false)) { // expand a chunk if possible
+ // If expand() failed we can just return, it had already called connection->emitReplyError()
+ return;
+ }
+#endif
+ }
+ }
+ // still in ReadingDataState? This function will be called again by the socket's readyRead
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
+ break;
+
+ // everything done, fall through
+ }
+ case QHttpNetworkReplyPrivate::AllDoneState:
+ allDone();
+ break;
+ 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();
+ close();
+ reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
+ emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ } else {
+ reconnectAttempts--;
+ reply->d_func()->clear();
+ reply->d_func()->connection = connection;
+ reply->d_func()->connectionChannel = this;
+ closeAndResendCurrentRequest();
+ }
+}
+
+bool QHttpNetworkConnectionChannel::ensureConnection()
+{
+ QAbstractSocket::SocketState socketState = socket->state();
+
+ // resend this request after we receive the disconnected signal
+ if (socketState == QAbstractSocket::ClosingState) {
+ if (reply)
+ resendCurrent = true;
+ return false;
+ }
+
+ // already trying to connect?
+ if (socketState == QAbstractSocket::HostLookupState ||
+ socketState == QAbstractSocket::ConnectingState) {
+ return false;
+ }
+
+ // make sure that this socket is in a connected state, if not initiate
+ // connection to the host.
+ if (socketState != QAbstractSocket::ConnectedState) {
+ // connect to the host if not already connected.
+ state = QHttpNetworkConnectionChannel::ConnectingState;
+ pendingEncrypt = ssl;
+
+ // reset state
+ pipeliningSupported = PipeliningSupportUnknown;
+
+ // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
+ // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
+ // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
+ // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
+ // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
+ // the phase is reset to Start.
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
+ if (priv && priv->phase == QAuthenticatorPrivate::Done)
+ priv->phase = QAuthenticatorPrivate::Start;
+ priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
+ if (priv && priv->phase == QAuthenticatorPrivate::Done)
+ priv->phase = QAuthenticatorPrivate::Start;
+
+ QString connectHost = connection->d_func()->hostName;
+ qint16 connectPort = connection->d_func()->port;
+
+#ifndef QT_NO_NETWORKPROXY
+ // HTTPS always use transparent proxy.
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
+ connectHost = connection->d_func()->networkProxy.hostName();
+ connectPort = connection->d_func()->networkProxy.port();
+ }
+#endif
+ 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 {
+ // 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;
+ }
+ return true;
+}
+
+
+#ifndef QT_NO_COMPRESS
+bool QHttpNetworkConnectionChannel::expand(bool dataComplete)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(reply);
+
+ qint64 total = reply->d_func()->compressedData.size();
+ if (total >= CHUNK || dataComplete) {
+ // uncompress the data
+ QByteArray content, inflated;
+ content = reply->d_func()->compressedData;
+ reply->d_func()->compressedData.clear();
+
+ int ret = Z_OK;
+ if (content.size())
+ ret = reply->d_func()->gunzipBodyPartially(content, inflated);
+ int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK;
+ if (ret >= retCheck) {
+ if (inflated.size()) {
+ reply->d_func()->totalProgress += inflated.size();
+ reply->d_func()->appendUncompressedReplyData(inflated);
+ if (reply->d_func()->shouldEmitSignals()) {
+ // 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();
+ emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
+ }
+ }
+ } else {
+ connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
+ return false;
+ }
+ }
+ return true;
+}
+#endif
+
+
+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) {
+ 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();
+ bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
+ 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 (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;
+
+ // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
+ 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
+ // finished request.
+ // Note that this may trigger a segfault at some other point. But then we can fix the underlying
+ // problem.
+ if (!resendCurrent) {
+ request = QHttpNetworkRequest();
+ reply = 0;
+ }
+
+ // move next from pipeline to current request
+ if (!alreadyPipelinedRequests.isEmpty()) {
+ if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
+ // move the pipelined ones back to the main queue
+ requeueCurrentlyPipelinedRequests();
+ close();
+ } else {
+ // there were requests pipelined in and we can continue
+ HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
+
+ request = messagePair.first;
+ reply = messagePair.second;
+ state = QHttpNetworkConnectionChannel::ReadingState;
+ resendCurrent = false;
+
+ written = 0; // message body, excluding the header, irrelevant here
+ bytesTotal = 0; // message body total, excluding the header, irrelevant here
+
+ // pipeline even more
+ connection->d_func()->fillPipeline(socket);
+
+ // continue reading
+ //_q_receiveReply();
+ // this was wrong, allDone gets called from that function anyway.
+ }
+ } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
+ // this is weird. we had nothing pipelined but still bytes available. better close it.
+ //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);
+ }
+}
+
+void QHttpNetworkConnectionChannel::detectPipeliningSupport()
+{
+ Q_ASSERT(reply);
+ // detect HTTP Pipelining support
+ QByteArray serverHeaderField;
+ if (
+ // check for HTTP/1.1
+ (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
+ // check for not having connection close
+ && (!reply->d_func()->isConnectionCloseEnabled())
+ // check if it is still connected
+ && (socket->state() == QAbstractSocket::ConnectedState)
+ // check for broken servers in server reply header
+ // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
+ && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
+ && (!serverHeaderField.contains("Microsoft-IIS/5."))
+ && (!serverHeaderField.contains("Netscape-Enterprise/3."))
+ // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
+ && (!serverHeaderField.contains("WebLogic"))
+ ) {
+ pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
+ } else {
+ pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
+ }
+}
+
+// called when the connection broke and we need to queue some pipelined requests again
+void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
+{
+ for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
+ connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
+ alreadyPipelinedRequests.clear();
+
+ // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
+ // this function is called from _q_disconnected which is called because
+ // of ~QHttpNetworkConnectionPrivate
+ if (qobject_cast<QHttpNetworkConnection*>(connection))
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+}
+
+void QHttpNetworkConnectionChannel::handleStatus()
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(reply);
+
+ int statusCode = reply->statusCode();
+ bool resend = false;
+
+ switch (statusCode) {
+ case 401: // auth required
+ case 407: // proxy auth required
+ if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
+ if (resend) {
+ if (!resetUploadData())
+ break;
+
+ reply->d_func()->eraseData();
+
+ if (alreadyPipelinedRequests.isEmpty()) {
+ // this does a re-send without closing the connection
+ resendCurrent = true;
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ } else {
+ // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
+ closeAndResendCurrentRequest();
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+ }
+ } else {
+ emit reply->headerChanged();
+ emit reply->readyRead();
+ QNetworkReply::NetworkError errorCode = (statusCode == 407)
+ ? QNetworkReply::ProxyAuthenticationRequiredError
+ : QNetworkReply::AuthenticationRequiredError;
+ reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
+ emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
+ }
+ break;
+ default:
+ if (qobject_cast<QHttpNetworkConnection*>(connection))
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+}
+
+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;
+
+ if (uploadByteDevice->reset()) {
+ written = 0;
+ return true;
+ } else {
+ connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
+ return false;
+ }
+}
+
+
+void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
+{
+ // this is only called for simple GET
+
+ QHttpNetworkRequest &request = pair.first;
+ QHttpNetworkReply *reply = pair.second;
+ 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 = true;
+
+#ifndef QT_NO_NETWORKPROXY
+ pipeline.append(QHttpNetworkRequestPrivate::header(request,
+ (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
+#else
+ pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
+#endif
+
+ 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();
+ if (reply)
+ resendCurrent = true;
+ if (qobject_cast<QHttpNetworkConnection*>(connection))
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+}
+
+bool QHttpNetworkConnectionChannel::isSocketBusy() const
+{
+ return (state & QHttpNetworkConnectionChannel::BusyState);
+}
+
+bool QHttpNetworkConnectionChannel::isSocketWriting() const
+{
+ return (state & QHttpNetworkConnectionChannel::WritingState);
+}
+
+bool QHttpNetworkConnectionChannel::isSocketWaiting() const
+{
+ return (state & QHttpNetworkConnectionChannel::WaitingState);
+}
+
+bool QHttpNetworkConnectionChannel::isSocketReading() const
+{
+ return (state & QHttpNetworkConnectionChannel::ReadingState);
+}
+
+//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)
+ _q_receiveReply();
+ }
+}
+
+void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
+{
+ Q_UNUSED(bytes);
+ // bytes have been written to the socket. write even more of them :)
+ if (isSocketWriting())
+ sendRequest();
+ // otherwise we do nothing
+}
+
+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) {
+ state = QHttpNetworkConnectionChannel::ReadingState;
+ _q_receiveReply();
+ }
+ } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
+ // re-sending request because the socket was in ClosingState
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+ state = QHttpNetworkConnectionChannel::IdleState;
+
+ requeueCurrentlyPipelinedRequests();
+ close();
+}
+
+
+void QHttpNetworkConnectionChannel::_q_connected()
+{
+ // improve performance since we get the request sent by the kernel ASAP
+ //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
+ // We have this commented out now. It did not have the effect we wanted. If we want to
+ // do this properly, Qt has to combine multiple HTTP requests into one buffer
+ // and send this to the kernel in one syscall and then the kernel immediately sends
+ // it as one TCP packet because of TCP_NODELAY.
+ // However, this code is currently not in Qt, so we rely on the kernel combining
+ // the requests into one TCP packet.
+
+ // not sure yet if it helps, but it makes sense
+ socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
+
+ pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
+
+ // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
+ //channels[i].reconnectAttempts = 2;
+ if (!pendingEncrypt) {
+ state = QHttpNetworkConnectionChannel::IdleState;
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ sendRequest();
+ }
+}
+
+
+void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
+{
+ if (!socket)
+ return;
+ QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
+
+ switch (socketError) {
+ case QAbstractSocket::HostNotFoundError:
+ errorCode = QNetworkReply::HostNotFoundError;
+ break;
+ case QAbstractSocket::ConnectionRefusedError:
+ errorCode = QNetworkReply::ConnectionRefusedError;
+ break;
+ case QAbstractSocket::RemoteHostClosedError:
+ // try to reconnect/resend before sending an error.
+ // while "Reading" the _q_disconnected() will handle this.
+ if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
+ if (reconnectAttempts-- > 0) {
+ closeAndResendCurrentRequest();
+ return;
+ } else {
+ errorCode = QNetworkReply::RemoteHostClosedError;
+ }
+ } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
+ if (!reply->d_func()->expectContent()) {
+ // No content expected, this is a valid way to have the connection closed by the server
+ return;
+ }
+ if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
+ // There was no content-length header and it's not chunked encoding,
+ // so this is a valid way to have the connection closed by the server
+ return;
+ }
+ // ok, we got a disconnect even though we did not expect it
+ errorCode = QNetworkReply::RemoteHostClosedError;
+ } else {
+ errorCode = QNetworkReply::RemoteHostClosedError;
+ }
+ break;
+ case QAbstractSocket::SocketTimeoutError:
+ // try to reconnect/resend before sending an error.
+ if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
+ closeAndResendCurrentRequest();
+ return;
+ }
+ errorCode = QNetworkReply::TimeoutError;
+ break;
+ case QAbstractSocket::ProxyAuthenticationRequiredError:
+ errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
+ break;
+ case QAbstractSocket::SslHandshakeFailedError:
+ errorCode = QNetworkReply::SslHandshakeFailedError;
+ break;
+ default:
+ // all other errors are treated as NetworkError
+ errorCode = QNetworkReply::UnknownNetworkError;
+ break;
+ }
+ 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);
+ reply = 0;
+ }
+ // send the next request
+ QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
+
+ if (that) //signal emission triggered event loop
+ close();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* 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
+
+void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
+{
+ sendRequest();
+}
+
+#ifndef QT_NO_OPENSSL
+void QHttpNetworkConnectionChannel::_q_encrypted()
+{
+ if (!socket)
+ return; // ### error
+ state = QHttpNetworkConnectionChannel::IdleState;
+ pendingEncrypt = false;
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ sendRequest();
+}
+
+void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
+{
+ if (!socket)
+ return;
+ //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
+ // Also pause the connection because socket notifiers may fire while an user
+ // dialog is displaying
+ connection->d_func()->pauseConnection();
+ if (pendingEncrypt && !reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ emit reply->sslErrors(errors);
+ connection->d_func()->resumeConnection();
+}
+
+void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
+{
+ Q_UNUSED(bytes);
+ // bytes have been written to the socket. write even more of them :)
+ if (isSocketWriting())
+ sendRequest();
+ // otherwise we do nothing
+}
+
+#endif
+
+void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
+{
+ // Inlining this function in the header leads to compiler error on
+ // release-armv5, on at least timebox 9.2 and 10.1.
+ connection = c;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qhttpnetworkconnectionchannel_p.cpp"
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
new file mode 100644
index 0000000000..f27c6f5294
--- /dev/null
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKCONNECTIONCHANNEL_H
+#define QHTTPNETWORKCONNECTIONCHANNEL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/qabstractsocket.h>
+
+#include <private/qobject_p.h>
+#include <qauthenticator.h>
+#include <qnetworkproxy.h>
+#include <qbuffer.h>
+
+#include <private/qhttpnetworkheader_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qhttpnetworkreply_p.h>
+
+#include <private/qhttpnetworkconnection_p.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslsocket.h>
+# include <QtNetwork/qsslerror.h>
+#else
+# include <QtNetwork/qtcpsocket.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkRequest;
+class QHttpNetworkReply;
+class QByteArray;
+
+#ifndef HttpMessagePair
+typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
+#endif
+
+class QHttpNetworkConnectionChannel : public QObject {
+ Q_OBJECT
+public:
+ enum ChannelState {
+ IdleState = 0, // ready to send request
+ ConnectingState = 1, // connecting to host
+ WritingState = 2, // writing the data
+ WaitingState = 4, // waiting for reply
+ ReadingState = 8, // reading the reply
+ 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
+ qint64 written;
+ qint64 bytesTotal;
+ bool resendCurrent;
+ int lastStatus; // last status received on this channel
+ bool pendingEncrypt; // for https (send after encrypted)
+ int reconnectAttempts; // maximum 2 reconnection attempts
+ QAuthenticatorPrivate::Method authMethod;
+ QAuthenticatorPrivate::Method proxyAuthMethod;
+ QAuthenticator authenticator;
+ QAuthenticator proxyAuthenticator;
+#ifndef QT_NO_OPENSSL
+ 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 {
+ PipeliningSupportUnknown, // default for a new connection
+ PipeliningProbablySupported, // after having received a server response that indicates support
+ PipeliningNotSupported // currently not used
+ };
+ 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();
+
+ void setConnection(QHttpNetworkConnection *c);
+ QPointer<QHttpNetworkConnection> connection;
+
+ void init();
+ void close();
+
+ bool sendRequest();
+
+ bool ensureConnection();
+
+ bool expand(bool dataComplete);
+ void allDone(); // reply header + body have been read
+ void handleStatus(); // called from allDone()
+
+ bool resetUploadData(); // return true if resetting worked or there is no upload data
+
+ void handleUnexpectedEOF();
+ void closeAndResendCurrentRequest();
+
+ bool isSocketBusy() const;
+ bool isSocketWriting() const;
+ bool isSocketWaiting() const;
+ bool isSocketReading() const;
+
+ friend class QNetworkAccessHttpBackend;
+
+ protected slots:
+ void _q_receiveReply();
+ void _q_bytesWritten(qint64 bytes); // proceed sending
+ void _q_readyRead(); // pending data to read
+ void _q_disconnected(); // disconnected from host
+ void _q_connected(); // start sending request
+ void _q_error(QAbstractSocket::SocketError); // error from socket
+#ifndef QT_NO_NETWORKPROXY
+ void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
+#endif
+
+ void _q_uploadDataReadyRead();
+
+#ifndef QT_NO_OPENSSL
+ void _q_encrypted(); // start sending request (https)
+ void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
+ void _q_encryptedBytesWritten(qint64 bytes); // proceed sending
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif
diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp
new file mode 100644
index 0000000000..2e33b3736d
--- /dev/null
+++ b/src/network/access/qhttpnetworkheader.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkheader_p.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
+ :url(newUrl)
+{
+}
+
+QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
+ :QSharedData(other)
+{
+ url = other.url;
+ fields = other.fields;
+}
+
+qint64 QHttpNetworkHeaderPrivate::contentLength() const
+{
+ bool ok = false;
+ // We are not using the headerField() method here because servers might send us multiple content-length
+ // headers which is crap (see QTBUG-15311). Therefore just take the first content-length header field.
+ QByteArray value;
+ QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
+ end = fields.constEnd();
+ for ( ; it != end; ++it)
+ if (qstricmp("content-length", it->first) == 0) {
+ value = it->second;
+ break;
+ }
+
+ qint64 length = value.toULongLong(&ok);
+ if (ok)
+ return length;
+ return -1; // the header field is not set
+}
+
+void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
+{
+ setHeaderField("Content-Length", QByteArray::number(length));
+}
+
+QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+{
+ QList<QByteArray> allValues = headerFieldValues(name);
+ if (allValues.isEmpty())
+ return defaultValue;
+
+ QByteArray result;
+ bool first = true;
+ foreach (const QByteArray &value, allValues) {
+ if (!first)
+ result += ", ";
+ first = false;
+ result += value;
+ }
+ return result;
+}
+
+QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
+{
+ QList<QByteArray> result;
+ QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
+ end = fields.constEnd();
+ for ( ; it != end; ++it)
+ if (qstricmp(name.constData(), it->first) == 0)
+ result += it->second;
+
+ return result;
+}
+
+void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin();
+ while (it != fields.end()) {
+ if (qstricmp(name.constData(), it->first) == 0)
+ it = fields.erase(it);
+ else
+ ++it;
+ }
+ fields.append(qMakePair(name, data));
+}
+
+bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
+{
+ return (url == other.url);
+}
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
new file mode 100644
index 0000000000..caebf7f730
--- /dev/null
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKHEADER_H
+#define QHTTPNETWORKHEADER_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.
+//
+#ifndef QT_NO_HTTP
+
+#include <qshareddata.h>
+#include <qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QHttpNetworkHeader
+{
+public:
+ virtual ~QHttpNetworkHeader() {};
+ virtual QUrl url() const = 0;
+ virtual void setUrl(const QUrl &url) = 0;
+
+ virtual int majorVersion() const = 0;
+ virtual int minorVersion() const = 0;
+
+ virtual qint64 contentLength() const = 0;
+ virtual void setContentLength(qint64 length) = 0;
+
+ virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
+ virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
+ virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
+};
+
+class QHttpNetworkHeaderPrivate : public QSharedData
+{
+public:
+ QUrl url;
+ QList<QPair<QByteArray, QByteArray> > fields;
+
+ QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
+ QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
+ qint64 contentLength() const;
+ void setContentLength(qint64 length);
+
+ QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
+ QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+ bool operator==(const QHttpNetworkHeaderPrivate &other) const;
+
+};
+
+
+QT_END_NAMESPACE
+
+
+#endif // QT_NO_HTTP
+
+
+#endif // QHTTPNETWORKHEADER_H
+
+
+
+
+
+
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
new file mode 100644
index 0000000000..cb6c09f010
--- /dev/null
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -0,0 +1,951 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkreply_p.h"
+#include "qhttpnetworkconnection_p.h"
+
+#include <qbytearraymatcher.h>
+
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qsslkey.h>
+# include <QtNetwork/qsslcipher.h>
+# include <QtNetwork/qsslconfiguration.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
+ : QObject(*new QHttpNetworkReplyPrivate(url), parent)
+{
+}
+
+QHttpNetworkReply::~QHttpNetworkReply()
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection) {
+ d->connection->d_func()->removeReply(this);
+ }
+}
+
+QUrl QHttpNetworkReply::url() const
+{
+ return d_func()->url;
+}
+void QHttpNetworkReply::setUrl(const QUrl &url)
+{
+ Q_D(QHttpNetworkReply);
+ d->url = url;
+}
+
+qint64 QHttpNetworkReply::contentLength() const
+{
+ return d_func()->contentLength();
+}
+
+void QHttpNetworkReply::setContentLength(qint64 length)
+{
+ Q_D(QHttpNetworkReply);
+ d->setContentLength(length);
+}
+
+QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
+{
+ return d_func()->fields;
+}
+
+QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+{
+ return d_func()->headerField(name, defaultValue);
+}
+
+void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ Q_D(QHttpNetworkReply);
+ d->setHeaderField(name, data);
+}
+
+void QHttpNetworkReply::parseHeader(const QByteArray &header)
+{
+ Q_D(QHttpNetworkReply);
+ d->parseHeader(header);
+}
+
+QHttpNetworkRequest QHttpNetworkReply::request() const
+{
+ return d_func()->request;
+}
+
+void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
+{
+ Q_D(QHttpNetworkReply);
+ d->request = request;
+ d->ssl = request.isSsl();
+}
+
+int QHttpNetworkReply::statusCode() const
+{
+ return d_func()->statusCode;
+}
+
+void QHttpNetworkReply::setStatusCode(int code)
+{
+ Q_D(QHttpNetworkReply);
+ d->statusCode = code;
+}
+
+QString QHttpNetworkReply::errorString() const
+{
+ return d_func()->errorString;
+}
+
+QString QHttpNetworkReply::reasonPhrase() const
+{
+ return d_func()->reasonPhrase;
+}
+
+void QHttpNetworkReply::setErrorString(const QString &error)
+{
+ Q_D(QHttpNetworkReply);
+ d->errorString = error;
+}
+
+int QHttpNetworkReply::majorVersion() const
+{
+ return d_func()->majorVersion;
+}
+
+int QHttpNetworkReply::minorVersion() const
+{
+ return d_func()->minorVersion;
+}
+
+qint64 QHttpNetworkReply::bytesAvailable() const
+{
+ Q_D(const QHttpNetworkReply);
+ if (d->connection)
+ return d->connection->d_func()->uncompressedBytesAvailable(*this);
+ else
+ return -1;
+}
+
+qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
+{
+ Q_D(const QHttpNetworkReply);
+ if (d->connection)
+ return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
+ else
+ return -1;
+}
+
+bool QHttpNetworkReply::readAnyAvailable() const
+{
+ Q_D(const QHttpNetworkReply);
+ return (d->responseData.bufferCount() > 0);
+}
+
+QByteArray QHttpNetworkReply::readAny()
+{
+ Q_D(QHttpNetworkReply);
+ if (d->responseData.bufferCount() == 0)
+ return QByteArray();
+
+ // we'll take the last buffer, so schedule another read from http
+ if (d->downstreamLimited && d->responseData.bufferCount() == 1)
+ d->connection->d_func()->readMoreLater(this);
+ return d->responseData.read();
+}
+
+QByteArray QHttpNetworkReply::readAll()
+{
+ Q_D(QHttpNetworkReply);
+ return d->responseData.readAll();
+}
+
+void QHttpNetworkReply::setDownstreamLimited(bool dsl)
+{
+ Q_D(QHttpNetworkReply);
+ d->downstreamLimited = 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;
+}
+
+bool QHttpNetworkReply::isPipeliningUsed() const
+{
+ return d_func()->pipeliningUsed;
+}
+
+QHttpNetworkConnection* QHttpNetworkReply::connection()
+{
+ return d_func()->connection;
+}
+
+
+QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
+ : QHttpNetworkHeaderPrivate(newUrl)
+ , state(NothingDoneState)
+ , ssl(false)
+ , statusCode(100),
+ majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
+ chunkedTransferEncoding(false),
+ connectionCloseEnabled(true),
+ forceConnectionCloseEnabled(false),
+ currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
+ autoDecompress(false), responseData(), requestIsPrepared(false)
+ ,pipeliningUsed(false), downstreamLimited(false)
+ ,userProvidedDownloadBuffer(0)
+{
+}
+
+QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
+{
+}
+
+void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
+{
+ state = NothingDoneState;
+ statusCode = 100;
+ bodyLength = 0;
+ contentRead = 0;
+ totalProgress = 0;
+ currentChunkSize = 0;
+ currentChunkRead = 0;
+ connectionCloseEnabled = true;
+#ifndef QT_NO_COMPRESS
+ if (initInflate)
+ inflateEnd(&inflateStrm);
+#endif
+ initInflate = false;
+ streamEnd = false;
+ fields.clear();
+}
+
+// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
+void QHttpNetworkReplyPrivate::clear()
+{
+ connection = 0;
+ connectionChannel = 0;
+ autoDecompress = false;
+ clearHttpLayerInformation();
+}
+
+// QHttpNetworkReplyPrivate
+qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
+{
+ return (state != ReadingDataState ? 0 : fragment.size());
+}
+
+bool QHttpNetworkReplyPrivate::isGzipped()
+{
+ QByteArray encoding = headerField("content-encoding");
+ return qstricmp(encoding.constData(), "gzip") == 0;
+}
+
+void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
+{
+ // The header "Content-Encoding = gzip" is retained.
+ // Content-Length is removed since the actual one send by the server is for compressed data
+ QByteArray name("content-length");
+ QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
+ end = fields.end();
+ while (it != end) {
+ if (qstricmp(name.constData(), it->first.constData()) == 0) {
+ fields.erase(it);
+ break;
+ }
+ ++it;
+ }
+
+}
+
+bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
+{
+ challenge.clear();
+ // find out the type of authentication protocol requested.
+ QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
+ // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
+ QList<QByteArray> challenges = headerFieldValues(header);
+ for (int i = 0; i<challenges.size(); i++) {
+ QByteArray line = challenges.at(i);
+ // todo use qstrincmp
+ if (!line.toLower().startsWith("negotiate"))
+ challenge = line;
+ }
+ return !challenge.isEmpty();
+}
+
+QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
+{
+ // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
+ QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
+ QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
+ QList<QByteArray> challenges = headerFieldValues(header);
+ for (int i = 0; i<challenges.size(); i++) {
+ QByteArray line = challenges.at(i).trimmed().toLower();
+ if (method < QAuthenticatorPrivate::Basic
+ && line.startsWith("basic")) {
+ method = QAuthenticatorPrivate::Basic;
+ } else if (method < QAuthenticatorPrivate::Ntlm
+ && line.startsWith("ntlm")) {
+ method = QAuthenticatorPrivate::Ntlm;
+ } else if (method < QAuthenticatorPrivate::DigestMd5
+ && line.startsWith("digest")) {
+ method = QAuthenticatorPrivate::DigestMd5;
+ }
+ }
+ return method;
+}
+
+#ifndef QT_NO_COMPRESS
+bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
+{
+ int method = 0; // method byte
+ int flags = 0; // flags byte
+ bool ret = false;
+
+ // Assure two bytes in the buffer so we can peek ahead -- handle case
+ // where first byte of header is at the end of the buffer after the last
+ // gzip segment
+ pos = -1;
+ QByteArray &body = content;
+ int maxPos = body.size()-1;
+ if (maxPos < 1) {
+ return ret;
+ }
+
+ // Peek ahead to check the gzip magic header
+ if (body[0] != char(gz_magic[0]) ||
+ body[1] != char(gz_magic[1])) {
+ return ret;
+ }
+ pos += 2;
+ // Check the rest of the gzip header
+ if (++pos <= maxPos)
+ method = body[pos];
+ if (pos++ <= maxPos)
+ flags = body[pos];
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ return ret;
+ }
+
+ // Discard time, xflags and OS code:
+ pos += 6;
+ if (pos > maxPos)
+ return ret;
+ if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
+ unsigned len = (unsigned)body[++pos];
+ len += ((unsigned)body[++pos])<<8;
+ pos += len;
+ if (pos > maxPos)
+ return ret;
+ }
+ if ((flags & ORIG_NAME) != 0) { // skip the original file name
+ while(++pos <= maxPos && body[pos]) {}
+ }
+ if ((flags & COMMENT) != 0) { // skip the .gz file comment
+ while(++pos <= maxPos && body[pos]) {}
+ }
+ if ((flags & HEAD_CRC) != 0) { // skip the header crc
+ pos += 2;
+ if (pos > maxPos)
+ return ret;
+ }
+ ret = (pos < maxPos); // return failed, if no more bytes left
+ return ret;
+}
+
+int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
+{
+ int ret = Z_DATA_ERROR;
+ unsigned have;
+ unsigned char out[CHUNK];
+ int pos = -1;
+
+ if (!initInflate) {
+ // check the header
+ if (!gzipCheckHeader(compressed, pos))
+ return ret;
+ // allocate inflate state
+ inflateStrm.zalloc = Z_NULL;
+ inflateStrm.zfree = Z_NULL;
+ inflateStrm.opaque = Z_NULL;
+ inflateStrm.avail_in = 0;
+ inflateStrm.next_in = Z_NULL;
+ ret = inflateInit2(&inflateStrm, -MAX_WBITS);
+ if (ret != Z_OK)
+ return ret;
+ initInflate = true;
+ streamEnd = false;
+ }
+
+ //remove the header.
+ compressed.remove(0, pos+1);
+ // expand until deflate stream ends
+ inflateStrm.next_in = (unsigned char *)compressed.data();
+ inflateStrm.avail_in = compressed.size();
+ do {
+ inflateStrm.avail_out = sizeof(out);
+ inflateStrm.next_out = out;
+ ret = inflate(&inflateStrm, Z_NO_FLUSH);
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ // and fall through
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&inflateStrm);
+ initInflate = false;
+ return ret;
+ }
+ have = sizeof(out) - inflateStrm.avail_out;
+ inflated.append(QByteArray((const char *)out, have));
+ } while (inflateStrm.avail_out == 0);
+ // clean up and return
+ if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
+ inflateEnd(&inflateStrm);
+ initInflate = false;
+ }
+ streamEnd = (ret == Z_STREAM_END);
+ return ret;
+}
+#endif
+
+qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
+{
+ if (fragment.isEmpty()) {
+ // reserve bytes for the status line. This is better than always append() which reallocs the byte array
+ fragment.reserve(32);
+ }
+
+ qint64 bytes = 0;
+ char c;
+ qint64 haveRead = 0;
+
+ do {
+ haveRead = socket->read(&c, 1);
+ if (haveRead == -1)
+ 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++;
+
+ // allow both CRLF & LF (only) line endings
+ if (c == '\n') {
+ // remove the CR at the end
+ if (fragment.endsWith('\r')) {
+ fragment.truncate(fragment.length()-1);
+ }
+ bool ok = parseStatus(fragment);
+ state = ReadingHeaderState;
+ fragment.clear();
+ if (!ok) {
+ return -1;
+ }
+ break;
+ } else {
+ fragment.append(c);
+ }
+
+ // is this a valid reply?
+ if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
+ {
+ fragment.clear();
+ return -1;
+ }
+ } while (haveRead == 1);
+
+ return bytes;
+}
+
+bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
+{
+ // from RFC 2616:
+ // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+ // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+ // that makes: 'HTTP/n.n xxx Message'
+ // byte count: 0123456789012
+
+ static const int minLength = 11;
+ static const int dotPos = 6;
+ static const int spacePos = 8;
+ static const char httpMagic[] = "HTTP/";
+
+ if (status.length() < minLength
+ || !status.startsWith(httpMagic)
+ || status.at(dotPos) != '.'
+ || status.at(spacePos) != ' ') {
+ // I don't know how to parse this status line
+ return false;
+ }
+
+ // optimize for the valid case: defer checking until the end
+ majorVersion = status.at(dotPos - 1) - '0';
+ minorVersion = status.at(dotPos + 1) - '0';
+
+ int i = spacePos;
+ int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
+ const QByteArray code = status.mid(i + 1, j - i - 1);
+
+ bool ok;
+ statusCode = code.toInt(&ok);
+ reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
+
+ return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
+}
+
+qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
+{
+ if (fragment.isEmpty()) {
+ // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
+ // block is 381 bytes.
+ // reserve bytes. This is better than always append() which reallocs the byte array.
+ fragment.reserve(512);
+ }
+
+ qint64 bytes = 0;
+ char c = 0;
+ bool allHeaders = false;
+ qint64 haveRead = 0;
+ do {
+ haveRead = socket->read(&c, 1);
+ if (haveRead == 0) {
+ // read more later
+ break;
+ } else if (haveRead == -1) {
+ // connection broke down
+ return -1;
+ } else {
+ fragment.append(c);
+ bytes++;
+
+ if (c == '\n') {
+ // check for possible header endings. As per HTTP rfc,
+ // the header endings will be marked by CRLFCRLF. But
+ // we will allow CRLFCRLF, CRLFLF, LFLF
+ if (fragment.endsWith("\r\n\r\n")
+ || fragment.endsWith("\r\n\n")
+ || fragment.endsWith("\n\n"))
+ allHeaders = true;
+
+ // there is another case: We have no headers. Then the fragment equals just the line ending
+ if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
+ || (fragment.length() == 1 && fragment.endsWith("\n")))
+ allHeaders = true;
+ }
+ }
+ } while (!allHeaders && haveRead > 0);
+
+ // we received all headers now parse them
+ if (allHeaders) {
+ parseHeader(fragment);
+ state = ReadingDataState;
+ fragment.clear(); // next fragment
+ bodyLength = contentLength(); // cache the length
+
+ // cache isChunked() since it is called often
+ chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
+
+ // cache isConnectionCloseEnabled since it is called often
+ QByteArray connectionHeaderField = headerField("connection");
+ // check for explicit indication of close or the implicit connection close of HTTP/1.0
+ connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
+ headerField("proxy-connection").toLower().contains("close")) ||
+ (majorVersion == 1 && minorVersion == 0 && connectionHeaderField.isEmpty());
+ }
+ return bytes;
+}
+
+void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
+{
+ // see rfc2616, sec 4 for information about HTTP/1.1 headers.
+ // allows relaxed parsing here, accepts both CRLF & LF line endings
+ const QByteArrayMatcher lf("\n");
+ const QByteArrayMatcher colon(":");
+ int i = 0;
+ while (i < header.count()) {
+ int j = colon.indexIn(header, i); // field-name
+ if (j == -1)
+ break;
+ const QByteArray field = header.mid(i, j - i).trimmed();
+ j++;
+ // any number of LWS is allowed before and after the value
+ QByteArray value;
+ do {
+ i = lf.indexIn(header, j);
+ if (i == -1)
+ break;
+ if (!value.isEmpty())
+ value += ' ';
+ // check if we have CRLF or only LF
+ bool hasCR = (i && header[i-1] == '\r');
+ int length = i -(hasCR ? 1: 0) - j;
+ value += header.mid(j, length).trimmed();
+ j = ++i;
+ } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
+ if (i == -1)
+ break; // something is wrong
+
+ fields.append(qMakePair(field, value));
+ }
+}
+
+bool QHttpNetworkReplyPrivate::isChunked()
+{
+ return chunkedTransferEncoding;
+}
+
+bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
+{
+ return connectionCloseEnabled || forceConnectionCloseEnabled;
+}
+
+// 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(), toBeRead);
+ if (haveRead == -1) {
+ bd.clear();
+ return 0; // ### error checking here;
+ }
+ bd.resize(haveRead);
+
+ rb->append(bd);
+
+ if (contentRead + haveRead == bodyLength) {
+ state = AllDoneState;
+ }
+
+ contentRead += haveRead;
+ return haveRead;
+}
+
+
+qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
+{
+ qint64 bytes = 0;
+ if (isChunked()) {
+ // 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(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(socket);
+ Q_ASSERT(out);
+
+ int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
+
+ while (toBeRead > 0) {
+ QByteArray byteData;
+ byteData.resize(toBeRead);
+ qint64 haveRead = socket->read(byteData.data(), byteData.size());
+ if (haveRead <= 0) {
+ // ### error checking here
+ byteData.clear();
+ return bytes;
+ }
+
+ byteData.resize(haveRead);
+ out->append(byteData);
+ bytes += haveRead;
+ size -= haveRead;
+
+ toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
+ }
+ return bytes;
+
+}
+
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
+{
+ qint64 bytes = 0;
+ 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];
+ // 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;
+ }
+ // Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
+ bytes += getChunkSize(socket, &currentChunkSize);
+ if (currentChunkSize == -1)
+ break;
+ }
+ // if the chunk size is 0, end of the stream
+ if (currentChunkSize == 0) {
+ state = AllDoneState;
+ break;
+ }
+
+ // 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;
+
+ // ### error checking here
+
+ }
+ return bytes;
+}
+
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
+{
+ qint64 bytes = 0;
+ char crlf[2];
+ *chunkSize = -1;
+
+ int bytesAvailable = socket->bytesAvailable();
+ // FIXME rewrite to permanent loop without bytesAvailable
+ while (bytesAvailable > bytes) {
+ 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 += socket->read(crlf, 1); // read the \r or \n
+ if (crlf[0] == '\r')
+ bytes += socket->read(crlf, 1); // read the \n
+ bool ok = false;
+ // ignore the chunk-extension
+ fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
+ *chunkSize = fragment.toLong(&ok, 16);
+ fragment.clear();
+ break; // size done
+ } else {
+ // read the fragment to the buffer
+ char c = 0;
+ qint64 haveRead = socket->read(&c, 1);
+ if (haveRead < 0) {
+ return -1; // FIXME
+ }
+ bytes += haveRead;
+ fragment.append(c);
+ }
+ }
+
+ return bytes;
+}
+
+void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba)
+{
+ responseData.append(qba);
+
+ // clear the original! helps with implicit sharing and
+ // avoiding memcpy when the user is reading the data
+ qba.clear();
+}
+
+void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data)
+{
+ responseData.append(data);
+
+ // clear the original! helps with implicit sharing and
+ // avoiding memcpy when the user is reading the data
+ data.clear();
+}
+
+void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data)
+{
+ // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer
+ // instead of one QByteArray.
+ for(int i = 0; i < data.bufferCount(); i++) {
+ QByteArray &byteData = data[i];
+ compressedData.append(byteData.constData(), byteData.size());
+ }
+ data.clear();
+}
+
+
+bool QHttpNetworkReplyPrivate::shouldEmitSignals()
+{
+ // for 401 & 407 don't emit the data signals. Content along with these
+ // responses are send only if the authentication fails.
+ return (statusCode != 401 && statusCode != 407);
+}
+
+bool QHttpNetworkReplyPrivate::expectContent()
+{
+ // check whether we can expect content after the headers (rfc 2616, sec4.4)
+ if ((statusCode >= 100 && statusCode < 200)
+ || statusCode == 204 || statusCode == 304)
+ return false;
+ if (request.operation() == QHttpNetworkRequest::Head)
+ return !shouldEmitSignals();
+ qint64 expectedContentLength = contentLength();
+ if (expectedContentLength == 0)
+ return false;
+ if (expectedContentLength == -1 && bodyLength == 0) {
+ // The content-length header was stripped, but its value was 0.
+ // This would be the case for an explicitly zero-length compressed response.
+ return false;
+ }
+ return true;
+}
+
+void QHttpNetworkReplyPrivate::eraseData()
+{
+ compressedData.clear();
+ responseData.clear();
+}
+
+
+// SSL support below
+#ifndef QT_NO_OPENSSL
+
+QSslConfiguration QHttpNetworkReply::sslConfiguration() const
+{
+ Q_D(const QHttpNetworkReply);
+
+ if (!d->connectionChannel)
+ return QSslConfiguration();
+
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
+ if (!sslSocket)
+ return QSslConfiguration();
+
+ return sslSocket->sslConfiguration();
+}
+
+void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection)
+ d->connection->setSslConfiguration(config);
+}
+
+void QHttpNetworkReply::ignoreSslErrors()
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection)
+ d->connection->ignoreSslErrors();
+}
+
+void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ Q_D(QHttpNetworkReply);
+ if (d->connection)
+ d->connection->ignoreSslErrors(errors);
+}
+
+
+#endif //QT_NO_OPENSSL
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
new file mode 100644
index 0000000000..cc0f671d2a
--- /dev/null
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKREPLY_H
+#define QHTTPNETWORKREPLY_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 <qplatformdefs.h>
+#ifndef QT_NO_HTTP
+
+#ifndef QT_NO_COMPRESS
+# include <zlib.h>
+static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
+// gzip flag byte
+#define HEAD_CRC 0x02 // bit 1 set: header CRC present
+#define EXTRA_FIELD 0x04 // bit 2 set: extra field present
+#define ORIG_NAME 0x08 // bit 3 set: original file name present
+#define COMMENT 0x10 // bit 4 set: file comment present
+#define RESERVED 0xE0 // bits 5..7: reserved
+#define CHUNK 16384
+#endif
+
+#include <QtNetwork/qtcpsocket.h>
+// it's safe to include these even if SSL support is not enabled
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qsslerror.h>
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <qbuffer.h>
+
+#include <private/qobject_p.h>
+#include <private/qhttpnetworkheader_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qauthenticator_p.h>
+#include <private/qringbuffer_p.h>
+#include <private/qbytedata_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkConnection;
+class QHttpNetworkConnectionChannel;
+class QHttpNetworkRequest;
+class QHttpNetworkConnectionPrivate;
+class QHttpNetworkReplyPrivate;
+class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
+{
+ Q_OBJECT
+public:
+
+ explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0);
+ virtual ~QHttpNetworkReply();
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ qint64 contentLength() const;
+ void setContentLength(qint64 length);
+
+ QList<QPair<QByteArray, QByteArray> > header() const;
+ QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+ void parseHeader(const QByteArray &header); // mainly for testing
+
+ QHttpNetworkRequest request() const;
+ void setRequest(const QHttpNetworkRequest &request);
+
+ int statusCode() const;
+ void setStatusCode(int code);
+
+ QString errorString() const;
+ void setErrorString(const QString &error);
+
+ QString reasonPhrase() const;
+
+ 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;
+
+ QHttpNetworkConnection* connection();
+
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration sslConfiguration() const;
+ void setSslConfiguration(const QSslConfiguration &config);
+ void ignoreSslErrors();
+ void ignoreSslErrors(const QList<QSslError> &errors);
+
+Q_SIGNALS:
+ void sslErrors(const QList<QSslError> &errors);
+#endif
+
+Q_SIGNALS:
+ void readyRead();
+ 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);
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
+#endif
+ void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
+private:
+ Q_DECLARE_PRIVATE(QHttpNetworkReply)
+ friend class QHttpNetworkConnection;
+ friend class QHttpNetworkConnectionPrivate;
+ friend class QHttpNetworkConnectionChannel;
+};
+
+
+class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
+{
+public:
+ QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
+ ~QHttpNetworkReplyPrivate();
+ qint64 readStatus(QAbstractSocket *socket);
+ bool parseStatus(const QByteArray &status);
+ 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(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);
+ void appendCompressedReplyData(QByteDataBuffer &data);
+
+ bool shouldEmitSignals();
+ bool expectContent();
+ void eraseData();
+
+ qint64 bytesAvailable() const;
+ bool isChunked();
+ bool isConnectionCloseEnabled();
+ bool isGzipped();
+#ifndef QT_NO_COMPRESS
+ bool gzipCheckHeader(QByteArray &content, int &pos);
+ int gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated);
+#endif
+ void removeAutoDecompressHeader();
+
+ enum ReplyState {
+ NothingDoneState,
+ ReadingStatusState,
+ ReadingHeaderState,
+ ReadingDataState,
+ AllDoneState
+ } state;
+
+ QHttpNetworkRequest request;
+ bool ssl;
+ int statusCode;
+ int majorVersion;
+ int minorVersion;
+ QString errorString;
+ QString reasonPhrase;
+ qint64 bodyLength;
+ qint64 contentRead;
+ qint64 totalProgress;
+ QByteArray fragment; // used for header, status, chunk header etc, not for reply data
+ bool chunkedTransferEncoding;
+ bool connectionCloseEnabled;
+ bool forceConnectionCloseEnabled;
+ qint64 currentChunkSize;
+ qint64 currentChunkRead;
+ QPointer<QHttpNetworkConnection> connection;
+ QPointer<QHttpNetworkConnectionChannel> connectionChannel;
+ bool initInflate;
+ bool streamEnd;
+#ifndef QT_NO_COMPRESS
+ z_stream inflateStrm;
+#endif
+ bool autoDecompress;
+
+ QByteDataBuffer responseData; // uncompressed body
+ QByteArray compressedData; // compressed body (temporary)
+ bool requestIsPrepared;
+
+ bool pipeliningUsed;
+ bool downstreamLimited;
+
+ char* userProvidedDownloadBuffer;
+};
+
+
+
+
+QT_END_NAMESPACE
+
+//Q_DECLARE_METATYPE(QHttpNetworkReply)
+
+#endif // QT_NO_HTTP
+
+
+#endif // QHTTPNETWORKREPLY_H
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
new file mode 100644
index 0000000000..2c67e87623
--- /dev/null
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpnetworkrequest_p.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
+ QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
+ : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
+ autoDecompress(false), pipeliningAllowed(false), withCredentials(true)
+{
+}
+
+QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other)
+ : QHttpNetworkHeaderPrivate(other)
+{
+ operation = other.operation;
+ priority = other.priority;
+ uploadByteDevice = other.uploadByteDevice;
+ autoDecompress = other.autoDecompress;
+ pipeliningAllowed = other.pipeliningAllowed;
+ customVerb = other.customVerb;
+ withCredentials = other.withCredentials;
+ ssl = other.ssl;
+}
+
+QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
+{
+}
+
+bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const
+{
+ return QHttpNetworkHeaderPrivate::operator==(other)
+ && (operation == other.operation)
+ && (ssl == other.ssl)
+ && (uploadByteDevice == other.uploadByteDevice);
+}
+
+QByteArray QHttpNetworkRequestPrivate::methodName() const
+{
+ switch (operation) {
+ case QHttpNetworkRequest::Get:
+ return "GET";
+ break;
+ case QHttpNetworkRequest::Head:
+ return "HEAD";
+ break;
+ case QHttpNetworkRequest::Post:
+ return "POST";
+ break;
+ case QHttpNetworkRequest::Options:
+ return "OPTIONS";
+ break;
+ case QHttpNetworkRequest::Put:
+ return "PUT";
+ break;
+ case QHttpNetworkRequest::Delete:
+ return "DELETE";
+ break;
+ case QHttpNetworkRequest::Trace:
+ return "TRACE";
+ break;
+ case QHttpNetworkRequest::Connect:
+ return "CONNECT";
+ break;
+ case QHttpNetworkRequest::Custom:
+ return customVerb;
+ break;
+ default:
+ break;
+ }
+ return QByteArray();
+}
+
+QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
+{
+ QUrl::FormattingOptions format(QUrl::RemoveFragment);
+
+ // for POST, query data is send as content
+ if (operation == QHttpNetworkRequest::Post && !uploadByteDevice)
+ format |= QUrl::RemoveQuery;
+ // for requests through proxy, the Request-URI contains full url
+ if (throughProxy)
+ format |= QUrl::RemoveUserInfo;
+ else
+ format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
+ QByteArray uri = url.toEncoded(format);
+ if (uri.isEmpty() || (throughProxy && url.path().isEmpty()))
+ uri += '/';
+ return uri;
+}
+
+QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
+{
+ QList<QPair<QByteArray, QByteArray> > fields = request.header();
+ QByteArray ba;
+ ba.reserve(40 + fields.length()*25); // very rough lower bound estimation
+
+ ba += request.d->methodName();
+ ba += ' ';
+ ba += request.d->uri(throughProxy);
+
+ ba += " HTTP/";
+ ba += QByteArray::number(request.majorVersion());
+ ba += '.';
+ ba += QByteArray::number(request.minorVersion());
+ ba += "\r\n";
+
+ QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
+ QList<QPair<QByteArray, QByteArray> >::const_iterator endIt = fields.constEnd();
+ for (; it != endIt; ++it) {
+ ba += it->first;
+ ba += ": ";
+ ba += it->second;
+ ba += "\r\n";
+ }
+ if (request.d->operation == QHttpNetworkRequest::Post) {
+ // add content type, if not set in the request
+ if (request.headerField("content-type").isEmpty()) {
+ 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: ";
+ ba += QByteArray::number(query.size());
+ ba += "\r\n\r\n";
+ ba += query;
+ } else {
+ ba += "\r\n";
+ }
+ } else {
+ ba += "\r\n";
+ }
+ return ba;
+}
+
+
+// QHttpNetworkRequest
+
+QHttpNetworkRequest::QHttpNetworkRequest(const QUrl &url, Operation operation, Priority priority)
+ : d(new QHttpNetworkRequestPrivate(operation, priority, url))
+{
+}
+
+QHttpNetworkRequest::QHttpNetworkRequest(const QHttpNetworkRequest &other)
+ : QHttpNetworkHeader(other), d(other.d)
+{
+}
+
+QHttpNetworkRequest::~QHttpNetworkRequest()
+{
+}
+
+QUrl QHttpNetworkRequest::url() const
+{
+ return d->url;
+}
+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();
+}
+
+void QHttpNetworkRequest::setContentLength(qint64 length)
+{
+ d->setContentLength(length);
+}
+
+QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
+{
+ return d->fields;
+}
+
+QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+{
+ return d->headerField(name, defaultValue);
+}
+
+void QHttpNetworkRequest::setHeaderField(const QByteArray &name, const QByteArray &data)
+{
+ d->setHeaderField(name, data);
+}
+
+QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
+{
+ d = other.d;
+ return *this;
+}
+
+bool QHttpNetworkRequest::operator==(const QHttpNetworkRequest &other) const
+{
+ return d->operator==(*other.d);
+}
+
+QHttpNetworkRequest::Operation QHttpNetworkRequest::operation() const
+{
+ return d->operation;
+}
+
+void QHttpNetworkRequest::setOperation(Operation operation)
+{
+ d->operation = operation;
+}
+
+QByteArray QHttpNetworkRequest::customVerb() const
+{
+ return d->customVerb;
+}
+
+void QHttpNetworkRequest::setCustomVerb(const QByteArray &customVerb)
+{
+ d->customVerb = customVerb;
+}
+
+QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const
+{
+ return d->priority;
+}
+
+void QHttpNetworkRequest::setPriority(Priority priority)
+{
+ d->priority = priority;
+}
+
+bool QHttpNetworkRequest::isPipeliningAllowed() const
+{
+ return d->pipeliningAllowed;
+}
+
+void QHttpNetworkRequest::setPipeliningAllowed(bool b)
+{
+ d->pipeliningAllowed = b;
+}
+
+bool QHttpNetworkRequest::withCredentials() const
+{
+ return d->withCredentials;
+}
+
+void QHttpNetworkRequest::setWithCredentials(bool b)
+{
+ d->withCredentials = b;
+}
+
+void QHttpNetworkRequest::setUploadByteDevice(QNonContiguousByteDevice *bd)
+{
+ d->uploadByteDevice = bd;
+}
+
+QNonContiguousByteDevice* QHttpNetworkRequest::uploadByteDevice() const
+{
+ return d->uploadByteDevice;
+}
+
+int QHttpNetworkRequest::majorVersion() const
+{
+ return 1;
+}
+
+int QHttpNetworkRequest::minorVersion() const
+{
+ return 1;
+}
+
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
new file mode 100644
index 0000000000..c7e9b0f6f5
--- /dev/null
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPNETWORKREQUEST_H
+#define QHTTPNETWORKREQUEST_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.
+//
+#ifndef QT_NO_HTTP
+
+#include <private/qhttpnetworkheader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNonContiguousByteDevice;
+
+class QHttpNetworkRequestPrivate;
+class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader
+{
+public:
+ enum Operation {
+ Options,
+ Get,
+ Head,
+ Post,
+ Put,
+ Delete,
+ Trace,
+ Connect,
+ Custom
+ };
+
+ enum Priority {
+ HighPriority,
+ NormalPriority,
+ LowPriority
+ };
+
+ QHttpNetworkRequest(const QUrl &url = QUrl(), Operation operation = Get, Priority priority = NormalPriority);
+ QHttpNetworkRequest(const QHttpNetworkRequest &other);
+ virtual ~QHttpNetworkRequest();
+ QHttpNetworkRequest &operator=(const QHttpNetworkRequest &other);
+ bool operator==(const QHttpNetworkRequest &other) const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ qint64 contentLength() const;
+ void setContentLength(qint64 length);
+
+ QList<QPair<QByteArray, QByteArray> > header() const;
+ QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
+ void setHeaderField(const QByteArray &name, const QByteArray &data);
+
+ Operation operation() const;
+ void setOperation(Operation operation);
+
+ QByteArray customVerb() const;
+ void setCustomVerb(const QByteArray &customOperation);
+
+ Priority priority() const;
+ void setPriority(Priority priority);
+
+ bool isPipeliningAllowed() const;
+ void setPipeliningAllowed(bool b);
+
+ bool withCredentials() const;
+ void setWithCredentials(bool b);
+
+ bool isSsl() const;
+ void setSsl(bool);
+
+ void setUploadByteDevice(QNonContiguousByteDevice *bd);
+ QNonContiguousByteDevice* uploadByteDevice() const;
+
+private:
+ QSharedDataPointer<QHttpNetworkRequestPrivate> d;
+ friend class QHttpNetworkRequestPrivate;
+ friend class QHttpNetworkConnectionPrivate;
+ friend class QHttpNetworkConnectionChannel;
+};
+
+class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
+{
+public:
+ QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
+ QHttpNetworkRequest::Priority pri, const QUrl &newUrl = QUrl());
+ QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
+ ~QHttpNetworkRequestPrivate();
+ bool operator==(const QHttpNetworkRequestPrivate &other) const;
+ QByteArray methodName() const;
+ QByteArray uri(bool throughProxy) const;
+
+ static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
+
+ QHttpNetworkRequest::Operation operation;
+ QByteArray customVerb;
+ QHttpNetworkRequest::Priority priority;
+ mutable QNonContiguousByteDevice* uploadByteDevice;
+ bool autoDecompress;
+ bool pipeliningAllowed;
+ bool withCredentials;
+ bool ssl;
+};
+
+
+QT_END_NAMESPACE
+
+//Q_DECLARE_METATYPE(QHttpNetworkRequest)
+
+#endif // QT_NO_HTTP
+
+
+#endif // QHTTPNETWORKREQUEST_H
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
new file mode 100644
index 0000000000..99f9376766
--- /dev/null
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -0,0 +1,579 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#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"
+
+
+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
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
new file mode 100644
index 0000000000..752bc099e7
--- /dev/null
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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"
+
+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 // QHTTPTHREADDELEGATE_H
diff --git a/src/network/access/qnetworkaccessauthenticationmanager.cpp b/src/network/access/qnetworkaccessauthenticationmanager.cpp
new file mode 100644
index 0000000000..d2bf00accf
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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 0000000000..d2347b1722
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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
new file mode 100644
index 0000000000..6220abed02
--- /dev/null
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -0,0 +1,382 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessbackend_p.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qmutex.h"
+#include "QtNetwork/private/qnetworksession_p.h"
+
+#include "qnetworkaccesscachebackend_p.h"
+#include "qabstractnetworkcache.h"
+#include "qhostinfo.h"
+
+#include "private/qnoncontiguousbytedevice_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static bool factoryDataShutdown = false;
+class QNetworkAccessBackendFactoryData: public QList<QNetworkAccessBackendFactory *>
+{
+public:
+ QNetworkAccessBackendFactoryData() : mutex(QMutex::Recursive) { }
+ ~QNetworkAccessBackendFactoryData()
+ {
+ QMutexLocker locker(&mutex); // why do we need to lock?
+ factoryDataShutdown = true;
+ }
+
+ QMutex mutex;
+};
+Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
+
+QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
+{
+ QMutexLocker locker(&factoryData()->mutex);
+ factoryData()->append(this);
+}
+
+QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
+{
+ if (!factoryDataShutdown) {
+ QMutexLocker locker(&factoryData()->mutex);
+ factoryData()->removeAll(this);
+ }
+}
+
+QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request)
+{
+ if (!factoryDataShutdown) {
+ QMutexLocker locker(&factoryData()->mutex);
+ QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(),
+ end = factoryData()->constEnd();
+ while (it != end) {
+ QNetworkAccessBackend *backend = (*it)->create(op, request);
+ if (backend) {
+ backend->manager = this;
+ return backend; // found a factory that handled our request
+ }
+ ++it;
+ }
+ }
+ return 0;
+}
+
+QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
+{
+ if (reply->outgoingDataBuffer)
+ uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer));
+ else if (reply->outgoingData) {
+ uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingData));
+ } else {
+ return 0;
+ }
+
+ bool bufferDisallowed =
+ reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
+ QVariant(false)) == QVariant(true);
+ if (bufferDisallowed)
+ uploadByteDevice->disableReset();
+
+ // We want signal emissions only for normal asynchronous uploads
+ if (!isSynchronous())
+ connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
+
+ 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)
+ return;
+ reply->emitUploadProgress(bytesSent, bytesTotal);
+}
+
+QNetworkAccessBackend::QNetworkAccessBackend()
+ : manager(0)
+ , reply(0)
+ , synchronous(false)
+{
+}
+
+QNetworkAccessBackend::~QNetworkAccessBackend()
+{
+}
+
+void QNetworkAccessBackend::downstreamReadyWrite()
+{
+ // do nothing
+}
+
+void QNetworkAccessBackend::setDownstreamLimited(bool b)
+{
+ Q_UNUSED(b);
+ // do nothing
+}
+
+void QNetworkAccessBackend::copyFinished(QIODevice *)
+{
+ // do nothing
+}
+
+void QNetworkAccessBackend::ignoreSslErrors()
+{
+ // do nothing
+}
+
+void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ Q_UNUSED(errors);
+ // do nothing
+}
+
+void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
+{
+ // do nothing
+}
+
+void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
+{
+ // do nothing
+}
+
+QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
+{
+ return QNetworkCacheMetaData();
+}
+
+QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
+{
+ return reply->operation;
+}
+
+QNetworkRequest QNetworkAccessBackend::request() const
+{
+ return reply->request;
+}
+
+#ifndef QT_NO_NETWORKPROXY
+QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
+{
+ return reply->proxyList;
+}
+#endif
+
+QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
+{
+ if (!manager)
+ return 0;
+ return manager->networkCache;
+}
+
+void QNetworkAccessBackend::setCachingEnabled(bool enable)
+{
+ reply->setCachingEnabled(enable);
+}
+
+bool QNetworkAccessBackend::isCachingEnabled() const
+{
+ return reply->isCachingEnabled();
+}
+
+qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
+{
+ return reply->nextDownstreamBlockSize();
+}
+
+void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
+{
+ reply->appendDownstreamData(list);
+}
+
+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);
+}
+
+void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+{
+ reply->setCookedHeader(header, value);
+}
+
+bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
+{
+ return reply->q_func()->hasRawHeader(headerName);
+}
+
+QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
+{
+ return reply->q_func()->rawHeader(headerName);
+}
+
+QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
+{
+ return reply->q_func()->rawHeaderList();
+}
+
+void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
+{
+ reply->setRawHeader(headerName, headerValue);
+}
+
+QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
+{
+ return reply->q_func()->attribute(code);
+}
+
+void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
+{
+ if (value.isValid())
+ reply->attributes.insert(code, value);
+ else
+ reply->attributes.remove(code);
+}
+QUrl QNetworkAccessBackend::url() const
+{
+ return reply->url;
+}
+
+void QNetworkAccessBackend::setUrl(const QUrl &url)
+{
+ reply->url = url;
+}
+
+void QNetworkAccessBackend::finished()
+{
+ reply->finished();
+}
+
+void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
+{
+ reply->error(code, errorString);
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
+ QAuthenticator *authenticator)
+{
+ manager->proxyAuthenticationRequired(this, proxy, authenticator);
+}
+#endif
+
+void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
+{
+ manager->authenticationRequired(this, authenticator);
+}
+
+void QNetworkAccessBackend::metaDataChanged()
+{
+ reply->metaDataChanged();
+}
+
+void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
+{
+ reply->redirectionRequested(target);
+}
+
+void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
+{
+#ifndef QT_NO_OPENSSL
+ reply->sslErrors(errors);
+#else
+ Q_UNUSED(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
+ function once the session has been opened or the roaming process has finished.
+*/
+bool QNetworkAccessBackend::start()
+{
+ if (!manager->networkSession) {
+ open();
+ return true;
+ }
+
+ // This is not ideal.
+ const QString host = reply->url.host();
+ if (host == QLatin1String("localhost") ||
+ QHostAddress(host) == QHostAddress::LocalHost ||
+ QHostAddress(host) == QHostAddress::LocalHostIPv6) {
+ // Don't need an open session for localhost access.
+ open();
+ return true;
+ }
+
+ if (manager->networkSession->isOpen() &&
+ manager->networkSession->state() == QNetworkSession::Connected) {
+ //copy network session down to the backend
+ setProperty("_q_networksession", QVariant::fromValue(manager->networkSession));
+ open();
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
new file mode 100644
index 0000000000..644ae2d9c7
--- /dev/null
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSBACKEND_P_H
+#define QNETWORKACCESSBACKEND_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 "qnetworkreplyimpl_p.h"
+#include "QtCore/qobject.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QNetworkProxy;
+class QNetworkProxyQuery;
+class QNetworkRequest;
+class QUrl;
+class QUrlInfo;
+class QSslConfiguration;
+
+class QNetworkAccessManagerPrivate;
+class QNetworkReplyImplPrivate;
+class QAbstractNetworkCache;
+class QNetworkCacheMetaData;
+class QNonContiguousByteDevice;
+
+// Should support direct file upload from disk or download to disk.
+//
+// - The HTTP handler will use two QIODevices for communication (pull mechanism)
+// - KIO uses a pull mechanism too (data/dataReq signals)
+class QNetworkAccessBackend : public QObject
+{
+ Q_OBJECT
+public:
+ QNetworkAccessBackend();
+ virtual ~QNetworkAccessBackend();
+
+ // To avoid mistaking with QIODevice names, the functions here
+ // have different names. The Connection has two streams:
+ //
+ // - Upstream:
+ // The upstream uses a QNonContiguousByteDevice provided
+ // by the backend. This device emits the usual readyRead()
+ // signal when the backend has data available for the connection
+ // to write. The different backends can listen on this signal
+ // and then pull upload data from the QNonContiguousByteDevice and
+ // deal with it.
+ //
+ //
+ // - Downstream:
+ // Downstream is the data that is being read from this
+ // connection and is given to the user. Downstream operates in a
+ // semi-"push" mechanism: the Connection object pushes the data
+ // it gets from the network, but it should avoid writing too
+ // much if the data isn't being used fast enough. The value
+ // returned by suggestedDownstreamBlockSize() can be used to
+ // determine how much should be written at a time. The
+ // downstreamBytesConsumed() function will be called when the
+ // downstream buffer is consumed by the user -- the Connection
+ // may choose to re-fill it with more data it has ready or get
+ // more data from the network (for instance, by reading from its
+ // socket).
+
+ virtual void open() = 0;
+#ifndef QT_NO_BEARERMANAGEMENT
+ virtual bool start();
+#endif
+ virtual void closeDownstreamChannel() = 0;
+
+ // slot-like:
+ virtual void downstreamReadyWrite();
+ virtual void setDownstreamLimited(bool b);
+ virtual void copyFinished(QIODevice *);
+ virtual void ignoreSslErrors();
+ virtual void ignoreSslErrors(const QList<QSslError> &errors);
+
+ virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
+ virtual void setSslConfiguration(const QSslConfiguration &configuration);
+
+ virtual QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
+
+ // information about the request
+ QNetworkAccessManager::Operation operation() const;
+ QNetworkRequest request() const;
+#ifndef QT_NO_NETWORKPROXY
+ QList<QNetworkProxy> proxyList() const;
+#endif
+
+ QAbstractNetworkCache *networkCache() const;
+ void setCachingEnabled(bool enable);
+ bool isCachingEnabled() const;
+
+ // information about the reply
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ // "cooked" headers
+ QVariant header(QNetworkRequest::KnownHeaders header) const;
+ void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
+
+ // raw headers:
+ bool hasRawHeader(const QByteArray &headerName) const;
+ QList<QByteArray> rawHeaderList() const;
+ QByteArray rawHeader(const QByteArray &headerName) const;
+ void setRawHeader(const QByteArray &headerName, const QByteArray &value);
+
+ // attributes:
+ QVariant attribute(QNetworkRequest::Attribute code) const;
+ void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
+
+ bool isSynchronous() { return synchronous; }
+ void setSynchronous(bool sync) { synchronous = sync; }
+
+ // return true if the QNonContiguousByteDevice of the upload
+ // data needs to support reset(). Currently needed for HTTP.
+ // This will possibly enable buffering of the upload data.
+ virtual bool needsResetableUploadData() { return false; }
+
+ // Returns true if backend is able to resume downloads.
+ virtual bool canResume() const { return false; }
+ virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); }
+
+ virtual bool processRequestSynchronously() { return false; }
+
+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);
+
+protected slots:
+ void finished();
+ void error(QNetworkReply::NetworkError code, const QString &errorString);
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
+#endif
+ void authenticationRequired(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 QNetworkReplyImplPrivate;
+
+ bool synchronous;
+};
+
+class QNetworkAccessBackendFactory
+{
+public:
+ QNetworkAccessBackendFactory();
+ virtual ~QNetworkAccessBackendFactory();
+ virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp
new file mode 100644
index 0000000000..fd16591c3e
--- /dev/null
+++ b/src/network/access/qnetworkaccesscache.cpp
@@ -0,0 +1,379 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccesscache_p.h"
+#include "QtCore/qpointer.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qqueue.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkrequest.h"
+
+QT_BEGIN_NAMESPACE
+
+enum ExpiryTimeEnum {
+ ExpiryTime = 120
+};
+
+namespace {
+ struct Receiver
+ {
+ QPointer<QObject> object;
+ const char *member;
+ };
+}
+
+// idea copied from qcache.h
+struct QNetworkAccessCache::Node
+{
+ QDateTime timestamp;
+ QQueue<Receiver> receiverQueue;
+ QByteArray key;
+
+ Node *older, *newer;
+ CacheableObject *object;
+
+ int useCount;
+
+ Node()
+ : older(0), newer(0), object(0), useCount(0)
+ { }
+};
+
+QNetworkAccessCache::CacheableObject::CacheableObject()
+{
+ // leave the members uninitialized
+ // they must be initialized by the derived class's constructor
+}
+
+QNetworkAccessCache::CacheableObject::~CacheableObject()
+{
+#if 0 //def QT_DEBUG
+ if (!key.isEmpty() && Ptr()->hasEntry(key))
+ qWarning() << "QNetworkAccessCache: object" << (void*)this << "key" << key
+ << "destroyed without being removed from cache first!";
+#endif
+}
+
+void QNetworkAccessCache::CacheableObject::setExpires(bool enable)
+{
+ expires = enable;
+}
+
+void QNetworkAccessCache::CacheableObject::setShareable(bool enable)
+{
+ shareable = enable;
+}
+
+QNetworkAccessCache::QNetworkAccessCache()
+ : oldest(0), newest(0)
+{
+}
+
+QNetworkAccessCache::~QNetworkAccessCache()
+{
+ clear();
+}
+
+void QNetworkAccessCache::clear()
+{
+ NodeHash hashCopy = hash;
+ hash.clear();
+
+ // remove all entries
+ NodeHash::Iterator it = hashCopy.begin();
+ NodeHash::Iterator end = hashCopy.end();
+ for ( ; it != end; ++it) {
+ it->object->key.clear();
+ it->object->dispose();
+ }
+
+ // now delete:
+ hashCopy.clear();
+
+ timer.stop();
+
+ oldest = newest = 0;
+}
+
+/*!
+ Appens the entry given by @p key to the end of the linked list.
+ (i.e., makes it the newest entry)
+ */
+void QNetworkAccessCache::linkEntry(const QByteArray &key)
+{
+ NodeHash::Iterator it = hash.find(key);
+ if (it == hash.end())
+ return;
+
+ Node *const node = &it.value();
+ Q_ASSERT(node != oldest && node != newest);
+ Q_ASSERT(node->older == 0 && node->newer == 0);
+ Q_ASSERT(node->useCount == 0);
+
+ if (newest) {
+ Q_ASSERT(newest->newer == 0);
+ newest->newer = node;
+ node->older = newest;
+ }
+ if (!oldest) {
+ // there are no entries, so this is the oldest one too
+ oldest = node;
+ }
+
+ node->timestamp = QDateTime::currentDateTime().addSecs(ExpiryTime);
+ newest = node;
+}
+
+/*!
+ Removes the entry pointed by @p key from the linked list.
+ Returns true if the entry removed was the oldest one.
+ */
+bool QNetworkAccessCache::unlinkEntry(const QByteArray &key)
+{
+ NodeHash::Iterator it = hash.find(key);
+ if (it == hash.end())
+ return false;
+
+ Node *const node = &it.value();
+
+ bool wasOldest = false;
+ if (node == oldest) {
+ oldest = node->newer;
+ wasOldest = true;
+ }
+ if (node == newest)
+ newest = node->older;
+ if (node->older)
+ node->older->newer = node->newer;
+ if (node->newer)
+ node->newer->older = node->older;
+
+ node->newer = node->older = 0;
+ return wasOldest;
+}
+
+void QNetworkAccessCache::updateTimer()
+{
+ timer.stop();
+
+ if (!oldest)
+ return;
+
+ int interval = QDateTime::currentDateTime().secsTo(oldest->timestamp);
+ if (interval <= 0) {
+ interval = 0;
+ } else {
+ // round up the interval
+ interval = (interval + 15) & ~16;
+ }
+
+ timer.start(interval * 1000, this);
+}
+
+bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char *member)
+{
+ if (!connect(this, SIGNAL(entryReady(QNetworkAccessCache::CacheableObject*)),
+ target, member, Qt::QueuedConnection))
+ return false;
+
+ emit entryReady(node->object);
+ disconnect(SIGNAL(entryReady(QNetworkAccessCache::CacheableObject*)));
+
+ return true;
+}
+
+void QNetworkAccessCache::timerEvent(QTimerEvent *)
+{
+ // expire old items
+ QDateTime now = QDateTime::currentDateTime();
+
+ while (oldest && oldest->timestamp < now) {
+ Node *next = oldest->newer;
+ oldest->object->dispose();
+
+ hash.remove(oldest->key); // oldest gets deleted
+ oldest = next;
+ }
+
+ // fixup the list
+ if (oldest)
+ oldest->older = 0;
+ else
+ newest = 0;
+
+ updateTimer();
+}
+
+void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry)
+{
+ Q_ASSERT(!key.isEmpty());
+
+ if (unlinkEntry(key))
+ updateTimer();
+
+ Node &node = hash[key]; // create the entry in the hash if it didn't exist
+ if (node.useCount)
+ qWarning("QNetworkAccessCache::addEntry: overriding active cache entry '%s'",
+ key.constData());
+ if (node.object)
+ node.object->dispose();
+ node.object = entry;
+ node.object->key = key;
+ node.key = key;
+ node.useCount = 1;
+}
+
+bool QNetworkAccessCache::hasEntry(const QByteArray &key) const
+{
+ return hash.contains(key);
+}
+
+bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, const char *member)
+{
+ NodeHash::Iterator it = hash.find(key);
+ if (it == hash.end())
+ return false; // no such entry
+
+ Node *node = &it.value();
+
+ if (node->useCount > 0 && !node->object->shareable) {
+ // object is not shareable and is in use
+ // queue for later use
+ Q_ASSERT(node->older == 0 && node->newer == 0);
+ Receiver receiver;
+ receiver.object = target;
+ receiver.member = member;
+ node->receiverQueue.enqueue(receiver);
+
+ // request queued
+ return true;
+ } else {
+ // node not in use or is shareable
+ if (unlinkEntry(key))
+ updateTimer();
+
+ ++node->useCount;
+ return emitEntryReady(node, target, member);
+ }
+}
+
+QNetworkAccessCache::CacheableObject *QNetworkAccessCache::requestEntryNow(const QByteArray &key)
+{
+ NodeHash::Iterator it = hash.find(key);
+ if (it == hash.end())
+ return 0;
+ if (it->useCount > 0) {
+ if (it->object->shareable) {
+ ++it->useCount;
+ return it->object;
+ }
+
+ // object in use and not shareable
+ return 0;
+ }
+
+ // entry not in use, let the caller have it
+ bool wasOldest = unlinkEntry(key);
+ ++it->useCount;
+
+ if (wasOldest)
+ updateTimer();
+ return it->object;
+}
+
+void QNetworkAccessCache::releaseEntry(const QByteArray &key)
+{
+ NodeHash::Iterator it = hash.find(key);
+ if (it == hash.end()) {
+ qWarning("QNetworkAccessCache::releaseEntry: trying to release key '%s' that is not in cache",
+ key.constData());
+ return;
+ }
+
+ Node *node = &it.value();
+ Q_ASSERT(node->useCount > 0);
+
+ // are there other objects waiting?
+ if (!node->receiverQueue.isEmpty()) {
+ // queue another activation
+ Receiver receiver;
+ do {
+ receiver = node->receiverQueue.dequeue();
+ } while (receiver.object.isNull() && !node->receiverQueue.isEmpty());
+
+ if (!receiver.object.isNull()) {
+ emitEntryReady(node, receiver.object, receiver.member);
+ return;
+ }
+ }
+
+ if (!--node->useCount) {
+ // no objects waiting; add it back to the expiry list
+ if (node->object->expires)
+ linkEntry(key);
+
+ if (oldest == node)
+ updateTimer();
+ }
+}
+
+void QNetworkAccessCache::removeEntry(const QByteArray &key)
+{
+ NodeHash::Iterator it = hash.find(key);
+ if (it == hash.end()) {
+ qWarning("QNetworkAccessCache::removeEntry: trying to remove key '%s' that is not in cache",
+ key.constData());
+ return;
+ }
+
+ Node *node = &it.value();
+ if (unlinkEntry(key))
+ updateTimer();
+ if (node->useCount > 1)
+ qWarning("QNetworkAccessCache::removeEntry: removing active cache entry '%s'",
+ key.constData());
+
+ node->object->key.clear();
+ hash.remove(node->key);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccesscache_p.h b/src/network/access/qnetworkaccesscache_p.h
new file mode 100644
index 0000000000..c2975009d8
--- /dev/null
+++ b/src/network/access/qnetworkaccesscache_p.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSCACHE_P_H
+#define QNETWORKACCESSCACHE_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/qobject.h"
+#include "QtCore/qbasictimer.h"
+#include "QtCore/qbytearray.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qmetatype.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkRequest;
+class QUrl;
+
+// this class is not about caching files but about
+// caching objects used by QNetworkAccessManager, e.g. existing TCP connections
+// or credentials.
+class QNetworkAccessCache: public QObject
+{
+ Q_OBJECT
+public:
+ struct Node;
+ typedef QHash<QByteArray, Node> NodeHash;
+
+ class CacheableObject
+ {
+ friend class QNetworkAccessCache;
+ QByteArray key;
+ bool expires;
+ bool shareable;
+ public:
+ CacheableObject();
+ virtual ~CacheableObject();
+ virtual void dispose() = 0;
+ inline QByteArray cacheKey() const { return key; }
+
+ protected:
+ void setExpires(bool enable);
+ void setShareable(bool enable);
+ };
+
+ QNetworkAccessCache();
+ ~QNetworkAccessCache();
+
+ void clear();
+
+ void addEntry(const QByteArray &key, CacheableObject *entry);
+ bool hasEntry(const QByteArray &key) const;
+ bool requestEntry(const QByteArray &key, QObject *target, const char *member);
+ CacheableObject *requestEntryNow(const QByteArray &key);
+ void releaseEntry(const QByteArray &key);
+ void removeEntry(const QByteArray &key);
+
+signals:
+ void entryReady(QNetworkAccessCache::CacheableObject *);
+
+protected:
+ void timerEvent(QTimerEvent *);
+
+private:
+ // idea copied from qcache.h
+ NodeHash hash;
+ Node *oldest;
+ Node *newest;
+
+ QBasicTimer timer;
+
+ void linkEntry(const QByteArray &key);
+ bool unlinkEntry(const QByteArray &key);
+ void updateTimer();
+ bool emitEntryReady(Node *node, QObject *target, const char *member);
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QNetworkAccessCache::CacheableObject*)
+
+#endif
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
new file mode 100644
index 0000000000..13f4cd9cbb
--- /dev/null
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNETWORKACCESSCACHEBACKEND_DEBUG
+
+#include "qnetworkaccesscachebackend_p.h"
+#include "qabstractnetworkcache.h"
+#include "qfileinfo.h"
+#include "qurlinfo.h"
+#include "qdir.h"
+#include "qcoreapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
+ : QNetworkAccessBackend()
+ , device(0)
+{
+}
+
+QNetworkAccessCacheBackend::~QNetworkAccessCacheBackend()
+{
+}
+
+void QNetworkAccessCacheBackend::open()
+{
+ if (operation() != QNetworkAccessManager::GetOperation || !sendCacheContents()) {
+ QString msg = QCoreApplication::translate("QNetworkAccessCacheBackend", "Error opening %1")
+ .arg(this->url().toString());
+ error(QNetworkReply::ContentNotFoundError, msg);
+ setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
+ }
+ finished();
+}
+
+bool QNetworkAccessCacheBackend::sendCacheContents()
+{
+ setCachingEnabled(false);
+ QAbstractNetworkCache *nc = networkCache();
+ if (!nc)
+ return false;
+
+ QNetworkCacheMetaData item = nc->metaData(url());
+ if (!item.isValid())
+ return false;
+
+ 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)
+ setRawHeader(it->first, it->second);
+
+ // handle a possible redirect
+ QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirectionTarget.isValid()) {
+ setAttribute(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
+ redirectionRequested(redirectionTarget.toUrl());
+ }
+
+ // signal we're open
+ metaDataChanged();
+
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ QIODevice *contents = nc->data(url());
+ if (!contents)
+ return false;
+ contents->setParent(this);
+ writeDownstreamData(contents);
+ }
+
+#if defined(QNETWORKACCESSCACHEBACKEND_DEBUG)
+ qDebug() << "Successfully sent cache:" << url();
+#endif
+ return true;
+}
+
+void QNetworkAccessCacheBackend::closeDownstreamChannel()
+{
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ device->close();
+ delete device;
+ device = 0;
+ }
+}
+
+void QNetworkAccessCacheBackend::closeUpstreamChannel()
+{
+ Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
+}
+
+void QNetworkAccessCacheBackend::upstreamReadyRead()
+{
+ Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
+}
+
+void QNetworkAccessCacheBackend::downstreamReadyWrite()
+{
+ Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/network/access/qnetworkaccesscachebackend_p.h b/src/network/access/qnetworkaccesscachebackend_p.h
new file mode 100644
index 0000000000..eda140ce43
--- /dev/null
+++ b/src/network/access/qnetworkaccesscachebackend_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSCACHEBACKEND_P_H
+#define QNETWORKACCESSCACHEBACKEND_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 "qnetworkaccessbackend_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAccessCacheBackend : public QNetworkAccessBackend
+{
+
+public:
+ QNetworkAccessCacheBackend();
+ ~QNetworkAccessCacheBackend();
+
+ void open();
+ void closeDownstreamChannel();
+ void closeUpstreamChannel();
+
+ void upstreamReadyRead();
+ void downstreamReadyWrite();
+
+private:
+ bool sendCacheContents();
+ QIODevice *device;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKACCESSCACHEBACKEND_P_H
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
new file mode 100644
index 0000000000..ab60a72074
--- /dev/null
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -0,0 +1,284 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessdebugpipebackend_p.h"
+#include "QtCore/qdatastream.h"
+#include <QCoreApplication>
+#include "private/qnoncontiguousbytedevice_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BUILD_INTERNAL
+
+enum {
+ ReadBufferSize = 16384,
+ WriteBufferSize = ReadBufferSize
+};
+
+QNetworkAccessBackend *
+QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const
+{
+ // is it an operation we know of?
+ switch (op) {
+ case QNetworkAccessManager::GetOperation:
+ case QNetworkAccessManager::PutOperation:
+ break;
+
+ default:
+ // no, we can't handle this operation
+ return 0;
+ }
+
+ QUrl url = request.url();
+ if (url.scheme() == QLatin1String("debugpipe"))
+ return new QNetworkAccessDebugPipeBackend;
+ return 0;
+}
+
+QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
+ : bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
+ hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
+{
+}
+
+QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
+{
+ // this is signals disconnect, not network!
+ socket.disconnect(this); // we're not interested in the signals at this point
+}
+
+void QNetworkAccessDebugPipeBackend::open()
+{
+ socket.connectToHost(url().host(), url().port(12345));
+ socket.setReadBufferSize(ReadBufferSize);
+
+ // socket ready read -> we can push from socket to downstream
+ connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
+ connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError()));
+ connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
+ connect(&socket, SIGNAL(connected()), SLOT(socketConnected()));
+ // socket bytes written -> we can push more from upstream to socket
+ connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
+
+ bareProtocol = url().queryItemValue(QLatin1String("bare")) == QLatin1String("1");
+
+ if (operation() == QNetworkAccessManager::PutOperation) {
+ uploadByteDevice = createUploadByteDevice();
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
+ QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
+ }
+}
+
+void QNetworkAccessDebugPipeBackend::socketReadyRead()
+{
+ pushFromSocketToDownstream();
+}
+
+void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
+{
+ pushFromSocketToDownstream();
+}
+
+void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
+{
+ pushFromUpstreamToSocket();
+}
+
+void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
+{
+ pushFromUpstreamToSocket();
+}
+
+void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
+{
+ QByteArray buffer;
+
+ if (socket.state() == QAbstractSocket::ConnectingState) {
+ return;
+ }
+
+ forever {
+ if (hasDownloadFinished)
+ return;
+
+ buffer.resize(ReadBufferSize);
+ qint64 haveRead = socket.read(buffer.data(), ReadBufferSize);
+
+ if (haveRead == -1) {
+ hasDownloadFinished = true;
+ // this ensures a good last downloadProgress is emitted
+ setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
+ possiblyFinish();
+ break;
+ } else if (haveRead == 0) {
+ break;
+ } else {
+ // have read something
+ buffer.resize(haveRead);
+ bytesDownloaded += haveRead;
+
+ QByteDataBuffer list;
+ list.append(buffer);
+ buffer.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
+ }
+ }
+}
+
+void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
+{
+ // FIXME
+ if (operation() == QNetworkAccessManager::PutOperation) {
+ if (hasUploadFinished)
+ return;
+
+ forever {
+ if (socket.bytesToWrite() >= WriteBufferSize)
+ return;
+
+ qint64 haveRead;
+ const char *readPointer = uploadByteDevice->readPointer(WriteBufferSize, haveRead);
+ if (haveRead == -1) {
+ // EOF
+ hasUploadFinished = true;
+ emitReplyUploadProgress(bytesUploaded, bytesUploaded);
+ possiblyFinish();
+ break;
+ } else if (haveRead == 0 || readPointer == 0) {
+ // nothing to read right now, we will be called again later
+ break;
+ } else {
+ qint64 haveWritten;
+ haveWritten = socket.write(readPointer, haveRead);
+
+ if (haveWritten < 0) {
+ // write error!
+ QString msg = QCoreApplication::translate("QNetworkAccessDebugPipeBackend", "Write error writing to %1: %2")
+ .arg(url().toString(), socket.errorString());
+ error(QNetworkReply::ProtocolFailure, msg);
+ finished();
+ return;
+ } else {
+ uploadByteDevice->advanceReadPointer(haveWritten);
+ bytesUploaded += haveWritten;
+ emitReplyUploadProgress(bytesUploaded, -1);
+ }
+
+ //QCoreApplication::processEvents();
+
+ }
+ }
+ }
+}
+
+void QNetworkAccessDebugPipeBackend::possiblyFinish()
+{
+ if (hasEverythingFinished)
+ return;
+ hasEverythingFinished = true;
+
+ if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
+ socket.close();
+ finished();
+ } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
+ socket.close();
+ finished();
+ }
+
+
+}
+
+void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
+{
+ qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
+ //if (operation() == QNetworkAccessManager::GetOperation)
+ // socket.disconnectFromHost();
+}
+
+
+void QNetworkAccessDebugPipeBackend::socketError()
+{
+ qWarning("QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
+ QNetworkReply::NetworkError code;
+ switch (socket.error()) {
+ case QAbstractSocket::RemoteHostClosedError:
+ return; // socketDisconnected will be called
+
+ case QAbstractSocket::NetworkError:
+ code = QNetworkReply::UnknownNetworkError;
+ break;
+
+ default:
+ code = QNetworkReply::ProtocolFailure;
+ break;
+ }
+
+ error(code, QNetworkAccessDebugPipeBackend::tr("Socket error on %1: %2")
+ .arg(url().toString(), socket.errorString()));
+ finished();
+ disconnect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+
+}
+
+void QNetworkAccessDebugPipeBackend::socketDisconnected()
+{
+ pushFromSocketToDownstream();
+
+ if (socket.bytesToWrite() == 0) {
+ // normal close
+ } else {
+ // abnormal close
+ QString msg = QNetworkAccessDebugPipeBackend::tr("Remote host closed the connection prematurely on %1")
+ .arg(url().toString());
+ error(QNetworkReply::RemoteHostClosedError, msg);
+ finished();
+ }
+}
+
+void QNetworkAccessDebugPipeBackend::socketConnected()
+{
+}
+
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h
new file mode 100644
index 0000000000..c65857c3f8
--- /dev/null
+++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSDEBUGPIPEBACKEND_P_H
+#define QNETWORKACCESSDEBUGPIPEBACKEND_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 "qnetworkaccessbackend_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "qtcpsocket.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_BUILD_INTERNAL
+
+class QNetworkAccessDebugPipeBackend: public QNetworkAccessBackend
+{
+ Q_OBJECT
+public:
+ QNetworkAccessDebugPipeBackend();
+ virtual ~QNetworkAccessDebugPipeBackend();
+
+ virtual void open();
+ virtual void closeDownstreamChannel();
+
+ virtual void downstreamReadyWrite();
+
+protected:
+ void pushFromSocketToDownstream();
+ void pushFromUpstreamToSocket();
+ void possiblyFinish();
+ QNonContiguousByteDevice *uploadByteDevice;
+
+private slots:
+ void uploadReadyReadSlot();
+ void socketReadyRead();
+ void socketBytesWritten(qint64 bytes);
+ void socketError();
+ void socketDisconnected();
+ void socketConnected();
+
+private:
+ QTcpSocket socket;
+ bool bareProtocol;
+ bool hasUploadFinished;
+ bool hasDownloadFinished;
+ bool hasEverythingFinished;
+
+ qint64 bytesDownloaded;
+ qint64 bytesUploaded;
+};
+
+class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory
+{
+public:
+ virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const;
+};
+
+#endif // QT_BUILD_INTERNAL
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
new file mode 100644
index 0000000000..7ebf626b7d
--- /dev/null
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessfilebackend_p.h"
+#include "qfileinfo.h"
+#include "qurlinfo.h"
+#include "qdir.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+
+#include <QtCore/QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkAccessBackend *
+QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const
+{
+ // is it an operation we know of?
+ switch (op) {
+ case QNetworkAccessManager::GetOperation:
+ case QNetworkAccessManager::PutOperation:
+ break;
+
+ default:
+ // no, we can't handle this operation
+ return 0;
+ }
+
+ QUrl url = request.url();
+ if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0 || url.isLocalFile()) {
+ return new QNetworkAccessFileBackend;
+ } else if (!url.scheme().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));
+ if ((url.scheme().length()==1) && fi.exists())
+ qWarning("QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files");
+ if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
+ return new QNetworkAccessFileBackend;
+ }
+
+ return 0;
+}
+
+QNetworkAccessFileBackend::QNetworkAccessFileBackend()
+ : uploadByteDevice(0), totalBytes(0), hasUploadFinished(false)
+{
+}
+
+QNetworkAccessFileBackend::~QNetworkAccessFileBackend()
+{
+}
+
+void QNetworkAccessFileBackend::open()
+{
+ QUrl url = this->url();
+
+ if (url.host() == QLatin1String("localhost"))
+ url.setHost(QString());
+#if !defined(Q_OS_WIN)
+ // do not allow UNC paths on Unix
+ if (!url.host().isEmpty()) {
+ // we handle only local files
+ error(QNetworkReply::ProtocolInvalidOperationError,
+ QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()));
+ finished();
+ return;
+ }
+#endif // !defined(Q_OS_WIN)
+ if (url.path().isEmpty())
+ url.setPath(QLatin1String("/"));
+ setUrl(url);
+
+ QString fileName = url.toLocalFile();
+ if (fileName.isEmpty()) {
+ if (url.scheme() == QLatin1String("qrc"))
+ fileName = QLatin1Char(':') + url.path();
+ else
+ fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
+ }
+ file.setFileName(fileName);
+
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ if (!loadFileInfo())
+ return;
+ }
+
+ QIODevice::OpenMode mode;
+ switch (operation()) {
+ case QNetworkAccessManager::GetOperation:
+ mode = QIODevice::ReadOnly;
+ break;
+ case QNetworkAccessManager::PutOperation:
+ mode = QIODevice::WriteOnly | QIODevice::Truncate;
+ uploadByteDevice = createUploadByteDevice();
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
+ QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
+ break;
+ default:
+ Q_ASSERT_X(false, "QNetworkAccessFileBackend::open",
+ "Got a request operation I cannot handle!!");
+ return;
+ }
+
+ mode |= QIODevice::Unbuffered;
+ bool opened = file.open(mode);
+
+ // could we open the file?
+ if (!opened) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
+ .arg(this->url().toString(), file.errorString());
+
+ // why couldn't we open the file?
+ // if we're opening for reading, either it doesn't exist, or it's access denied
+ // if we're opening for writing, not existing means it's access denied too
+ if (file.exists() || operation() == QNetworkAccessManager::PutOperation)
+ error(QNetworkReply::ContentAccessDenied, msg);
+ else
+ error(QNetworkReply::ContentNotFoundError, msg);
+ finished();
+ }
+}
+
+void QNetworkAccessFileBackend::uploadReadyReadSlot()
+{
+ if (hasUploadFinished)
+ return;
+
+ forever {
+ qint64 haveRead;
+ const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
+ if (haveRead == -1) {
+ // EOF
+ hasUploadFinished = true;
+ file.flush();
+ file.close();
+ finished();
+ break;
+ } else if (haveRead == 0 || readPointer == 0) {
+ // nothing to read right now, we will be called again later
+ break;
+ } else {
+ qint64 haveWritten;
+ haveWritten = file.write(readPointer, haveRead);
+
+ if (haveWritten < 0) {
+ // write error!
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2")
+ .arg(url().toString(), file.errorString());
+ error(QNetworkReply::ProtocolFailure, msg);
+
+ finished();
+ return;
+ } else {
+ uploadByteDevice->advanceReadPointer(haveWritten);
+ }
+
+
+ file.flush();
+ }
+ }
+}
+
+void QNetworkAccessFileBackend::closeDownstreamChannel()
+{
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ file.close();
+ }
+}
+
+void QNetworkAccessFileBackend::downstreamReadyWrite()
+{
+ Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend",
+ "We're being told to download data but operation isn't GET!");
+
+ readMoreFromFile();
+}
+
+bool QNetworkAccessFileBackend::loadFileInfo()
+{
+ QFileInfo fi(file);
+ setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
+ setHeader(QNetworkRequest::ContentLengthHeader, fi.size());
+
+ // signal we're open
+ metaDataChanged();
+
+ if (fi.isDir()) {
+ error(QNetworkReply::ContentOperationNotPermittedError,
+ QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url().toString()));
+ finished();
+ return false;
+ }
+
+ return true;
+}
+
+bool QNetworkAccessFileBackend::readMoreFromFile()
+{
+ qint64 wantToRead;
+ while ((wantToRead = nextDownstreamBlockSize()) > 0) {
+ // ### FIXME!!
+ // Obtain a pointer from the ringbuffer!
+ // Avoid extra copy
+ QByteArray data;
+ data.reserve(wantToRead);
+ qint64 actuallyRead = file.read(data.data(), wantToRead);
+ if (actuallyRead <= 0) {
+ // EOF or error
+ if (file.error() != QFile::NoError) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
+ .arg(url().toString(), file.errorString());
+ error(QNetworkReply::ProtocolFailure, msg);
+
+ finished();
+ return false;
+ }
+
+ finished();
+ return true;
+ }
+
+ data.resize(actuallyRead);
+ totalBytes += actuallyRead;
+
+ QByteDataBuffer list;
+ list.append(data);
+ data.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h
new file mode 100644
index 0000000000..c51d2934b3
--- /dev/null
+++ b/src/network/access/qnetworkaccessfilebackend_p.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSFILEBACKEND_P_H
+#define QNETWORKACCESSFILEBACKEND_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 "qnetworkaccessbackend_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "QtCore/qfile.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAccessFileBackend: public QNetworkAccessBackend
+{
+ Q_OBJECT
+public:
+ QNetworkAccessFileBackend();
+ virtual ~QNetworkAccessFileBackend();
+
+ virtual void open();
+ virtual void closeDownstreamChannel();
+
+ virtual void downstreamReadyWrite();
+
+public slots:
+ void uploadReadyReadSlot();
+protected:
+ QNonContiguousByteDevice *uploadByteDevice;
+private:
+ QFile file;
+ qint64 totalBytes;
+ bool hasUploadFinished;
+
+ bool loadFileInfo();
+ bool readMoreFromFile();
+};
+
+class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory
+{
+public:
+ virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp
new file mode 100644
index 0000000000..3ad1961e46
--- /dev/null
+++ b/src/network/access/qnetworkaccessftpbackend.cpp
@@ -0,0 +1,382 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessftpbackend_p.h"
+#include "qnetworkaccessmanager_p.h"
+#include "QtNetwork/qauthenticator.h"
+#include "private/qnoncontiguousbytedevice_p.h"
+
+#ifndef QT_NO_FTP
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ DefaultFtpPort = 21
+};
+
+static QByteArray makeCacheKey(const QUrl &url)
+{
+ QUrl copy = url;
+ copy.setPort(url.port(DefaultFtpPort));
+ return "ftp-connection:" +
+ copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery |
+ QUrl::RemoveFragment);
+}
+
+QNetworkAccessBackend *
+QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const
+{
+ // is it an operation we know of?
+ switch (op) {
+ case QNetworkAccessManager::GetOperation:
+ case QNetworkAccessManager::PutOperation:
+ break;
+
+ default:
+ // no, we can't handle this operation
+ return 0;
+ }
+
+ QUrl url = request.url();
+ if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0)
+ return new QNetworkAccessFtpBackend;
+ return 0;
+}
+
+class QNetworkAccessCachedFtpConnection: public QFtp, public QNetworkAccessCache::CacheableObject
+{
+ // Q_OBJECT
+public:
+ QNetworkAccessCachedFtpConnection()
+ {
+ setExpires(true);
+ setShareable(false);
+ }
+
+ void dispose()
+ {
+ connect(this, SIGNAL(done(bool)), this, SLOT(deleteLater()));
+ close();
+ }
+};
+
+QNetworkAccessFtpBackend::QNetworkAccessFtpBackend()
+ : ftp(0), uploadDevice(0), totalBytes(0), helpId(-1), sizeId(-1), mdtmId(-1),
+ supportsSize(false), supportsMdtm(false), state(Idle)
+{
+}
+
+QNetworkAccessFtpBackend::~QNetworkAccessFtpBackend()
+{
+ disconnectFromFtp();
+}
+
+void QNetworkAccessFtpBackend::open()
+{
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy;
+ foreach (const QNetworkProxy &p, proxyList()) {
+ // use the first FTP proxy
+ // or no proxy at all
+ if (p.type() == QNetworkProxy::FtpCachingProxy
+ || p.type() == QNetworkProxy::NoProxy) {
+ proxy = p;
+ break;
+ }
+ }
+
+ // did we find an FTP proxy or a NoProxy?
+ if (proxy.type() == QNetworkProxy::DefaultProxy) {
+ // unsuitable proxies
+ error(QNetworkReply::ProxyNotFoundError,
+ tr("No suitable proxy found"));
+ finished();
+ return;
+ }
+
+#endif
+
+ QUrl url = this->url();
+ if (url.path().isEmpty()) {
+ url.setPath(QLatin1String("/"));
+ setUrl(url);
+ }
+ if (url.path().endsWith(QLatin1Char('/'))) {
+ error(QNetworkReply::ContentOperationNotPermittedError,
+ tr("Cannot open %1: is a directory").arg(url.toString()));
+ finished();
+ return;
+ }
+ state = LoggingIn;
+
+ QNetworkAccessCache* objectCache = QNetworkAccessManagerPrivate::getObjectCache(this);
+ QByteArray cacheKey = makeCacheKey(url);
+ 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());
+#endif
+ ftp->connectToHost(url.host(), url.port(DefaultFtpPort));
+ ftp->login(url.userName(), url.password());
+
+ objectCache->addEntry(cacheKey, ftp);
+ ftpConnectionReady(ftp);
+ }
+
+ // Put operation
+ if (operation() == QNetworkAccessManager::PutOperation) {
+ uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice());
+ uploadDevice->setParent(this);
+ }
+}
+
+void QNetworkAccessFtpBackend::closeDownstreamChannel()
+{
+ state = Disconnecting;
+ if (operation() == QNetworkAccessManager::GetOperation)
+#ifndef Q_OS_WINCE
+ abort();
+#else
+ exit(3);
+#endif
+}
+
+void QNetworkAccessFtpBackend::downstreamReadyWrite()
+{
+ if (state == Transferring && ftp && ftp->bytesAvailable())
+ ftpReadyRead();
+}
+
+void QNetworkAccessFtpBackend::ftpConnectionReady(QNetworkAccessCache::CacheableObject *o)
+{
+ ftp = static_cast<QNetworkAccessCachedFtpConnection *>(o);
+ connect(ftp, SIGNAL(done(bool)), SLOT(ftpDone()));
+ connect(ftp, SIGNAL(rawCommandReply(int,QString)), SLOT(ftpRawCommandReply(int,QString)));
+ connect(ftp, SIGNAL(readyRead()), SLOT(ftpReadyRead()));
+
+ // is the login process done already?
+ if (ftp->state() == QFtp::LoggedIn)
+ ftpDone();
+
+ // no, defer the actual operation until after we've logged in
+}
+
+void QNetworkAccessFtpBackend::disconnectFromFtp()
+{
+ state = Disconnecting;
+
+ if (ftp) {
+ disconnect(ftp, 0, this, 0);
+
+ QByteArray key = makeCacheKey(url());
+ QNetworkAccessManagerPrivate::getObjectCache(this)->releaseEntry(key);
+
+ ftp = 0;
+ }
+}
+
+void QNetworkAccessFtpBackend::ftpDone()
+{
+ // the last command we sent is done
+ if (state == LoggingIn && ftp->state() != QFtp::LoggedIn) {
+ if (ftp->state() == QFtp::Connected) {
+ // the login did not succeed
+ QUrl newUrl = url();
+ newUrl.setUserInfo(QString());
+ setUrl(newUrl);
+
+ QAuthenticator auth;
+ authenticationRequired(&auth);
+
+ if (!auth.isNull()) {
+ // try again:
+ newUrl.setUserName(auth.user());
+ ftp->login(auth.user(), auth.password());
+ return;
+ }
+
+ error(QNetworkReply::AuthenticationRequiredError,
+ tr("Logging in to %1 failed: authentication required")
+ .arg(url().host()));
+ } else {
+ // we did not connect
+ QNetworkReply::NetworkError code;
+ switch (ftp->error()) {
+ case QFtp::HostNotFound:
+ code = QNetworkReply::HostNotFoundError;
+ break;
+
+ case QFtp::ConnectionRefused:
+ code = QNetworkReply::ConnectionRefusedError;
+ break;
+
+ default:
+ code = QNetworkReply::ProtocolFailure;
+ break;
+ }
+
+ error(code, ftp->errorString());
+ }
+
+ // we're not connected, so remove the cache entry:
+ QByteArray key = makeCacheKey(url());
+ QNetworkAccessManagerPrivate::getObjectCache(this)->removeEntry(key);
+
+ disconnect(ftp, 0, this, 0);
+ ftp->dispose();
+ ftp = 0;
+
+ state = Disconnecting;
+ finished();
+ return;
+ }
+
+ // check for errors:
+ if (ftp->error() != QFtp::NoError) {
+ QString msg;
+ if (operation() == QNetworkAccessManager::GetOperation)
+ msg = tr("Error while downloading %1: %2");
+ else
+ msg = tr("Error while uploading %1: %2");
+ msg = msg.arg(url().toString(), ftp->errorString());
+
+ if (state == Statting)
+ // file probably doesn't exist
+ error(QNetworkReply::ContentNotFoundError, msg);
+ else
+ error(QNetworkReply::ContentAccessDenied, msg);
+
+ disconnectFromFtp();
+ finished();
+ }
+
+ if (state == LoggingIn) {
+ state = CheckingFeatures;
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ // send help command to find out if server supports "SIZE" and "MDTM"
+ QString command = url().path();
+ command.prepend(QLatin1String("%1 "));
+ helpId = ftp->rawCommand(QLatin1String("HELP")); // get supported commands
+ } else {
+ ftpDone();
+ }
+ } else if (state == CheckingFeatures) {
+ state = Statting;
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ // logged in successfully, send the stat requests (if supported)
+ QString command = url().path();
+ command.prepend(QLatin1String("%1 "));
+ if (supportsSize) {
+ ftp->rawCommand(QLatin1String("TYPE I"));
+ sizeId = ftp->rawCommand(command.arg(QLatin1String("SIZE"))); // get size
+ }
+ if (supportsMdtm)
+ mdtmId = ftp->rawCommand(command.arg(QLatin1String("MDTM"))); // get modified time
+ if (!supportsSize && !supportsMdtm)
+ ftpDone(); // no commands sent, move to the next state
+ } else {
+ ftpDone();
+ }
+ } else if (state == Statting) {
+ // statted successfully, send the actual request
+ emit metaDataChanged();
+ state = Transferring;
+
+ QFtp::TransferType type = QFtp::Binary;
+ if (operation() == QNetworkAccessManager::GetOperation) {
+ setCachingEnabled(true);
+ ftp->get(url().path(), 0, type);
+ } else {
+ ftp->put(uploadDevice, url().path(), type);
+ }
+
+ } else if (state == Transferring) {
+ // upload or download finished
+ disconnectFromFtp();
+ finished();
+ }
+}
+
+void QNetworkAccessFtpBackend::ftpReadyRead()
+{
+ QByteArray data = ftp->readAll();
+ QByteDataBuffer list;
+ list.append(data);
+ data.clear(); // important because of implicit sharing!
+ writeDownstreamData(list);
+}
+
+void QNetworkAccessFtpBackend::ftpRawCommandReply(int code, const QString &text)
+{
+ //qDebug() << "FTP reply:" << code << text;
+ int id = ftp->currentId();
+
+ if ((id == helpId) && ((code == 200) || (code == 214))) { // supported commands
+ // the "FEAT" ftp command would be nice here, but it is not part of the
+ // initial FTP RFC 959, neither ar "SIZE" nor "MDTM" (they are all specified
+ // in RFC 3659)
+ if (text.contains(QLatin1String("SIZE"), Qt::CaseSensitive))
+ supportsSize = true;
+ if (text.contains(QLatin1String("MDTM"), Qt::CaseSensitive))
+ supportsMdtm = true;
+ } else if (code == 213) { // file status
+ if (id == sizeId) {
+ // reply to the size command
+ setHeader(QNetworkRequest::ContentLengthHeader, text.toLongLong());
+#ifndef QT_NO_DATESTRING
+ } else if (id == mdtmId) {
+ QDateTime dt = QDateTime::fromString(text, QLatin1String("yyyyMMddHHmmss"));
+ setHeader(QNetworkRequest::LastModifiedHeader, dt);
+#endif
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FTP
diff --git a/src/network/access/qnetworkaccessftpbackend_p.h b/src/network/access/qnetworkaccessftpbackend_p.h
new file mode 100644
index 0000000000..ae5b16758d
--- /dev/null
+++ b/src/network/access/qnetworkaccessftpbackend_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSFTPBACKEND_P_H
+#define QNETWORKACCESSFTPBACKEND_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 "qnetworkaccessbackend_p.h"
+#include "qnetworkaccesscache_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "QtNetwork/qftp.h"
+
+#include "QtCore/qpointer.h"
+
+#ifndef QT_NO_FTP
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAccessFtpIODevice;
+class QNetworkAccessCachedFtpConnection;
+
+class QNetworkAccessFtpBackend: public QNetworkAccessBackend
+{
+ Q_OBJECT
+public:
+ enum State {
+ Idle,
+ //Connecting,
+ LoggingIn,
+ CheckingFeatures,
+ Statting,
+ Transferring,
+ Disconnecting
+ };
+
+ QNetworkAccessFtpBackend();
+ virtual ~QNetworkAccessFtpBackend();
+
+ virtual void open();
+ virtual void closeDownstreamChannel();
+
+ virtual void downstreamReadyWrite();
+
+ void disconnectFromFtp();
+
+public slots:
+ void ftpConnectionReady(QNetworkAccessCache::CacheableObject *object);
+ void ftpDone();
+ void ftpReadyRead();
+ void ftpRawCommandReply(int code, const QString &text);
+
+private:
+ friend class QNetworkAccessFtpIODevice;
+ QPointer<QNetworkAccessCachedFtpConnection> ftp;
+ QIODevice *uploadDevice;
+ qint64 totalBytes;
+ int helpId, sizeId, mdtmId;
+ bool supportsSize, supportsMdtm;
+ State state;
+};
+
+class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory
+{
+public:
+ virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FTP
+
+#endif
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
new file mode 100644
index 0000000000..c61911445b
--- /dev/null
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -0,0 +1,1191 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNETWORKACCESSHTTPBACKEND_DEBUG
+
+#include "qnetworkaccesshttpbackend_p.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkaccesscache_p.h"
+#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
+
+Q_DECLARE_METATYPE(QSharedPointer<char>)
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkProxy;
+
+static inline bool isSeparator(register char c)
+{
+ static const char separators[] = "()<>@,;:\\\"/[]?={}";
+ return isLWS(c) || strchr(separators, c) != 0;
+}
+
+// ### merge with nextField in cookiejar.cpp
+static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header)
+{
+ // The HTTP header is of the form:
+ // header = #1(directives)
+ // directives = token | value-directive
+ // value-directive = token "=" (token | quoted-string)
+ QHash<QByteArray, QByteArray> result;
+
+ int pos = 0;
+ while (true) {
+ // skip spaces
+ pos = nextNonWhitespace(header, pos);
+ if (pos == header.length())
+ return result; // end of parsing
+
+ // pos points to a non-whitespace
+ int comma = header.indexOf(',', pos);
+ int equal = header.indexOf('=', pos);
+ if (comma == pos || equal == pos)
+ // huh? Broken header.
+ return result;
+
+ // The key name is delimited by either a comma, an equal sign or the end
+ // of the header, whichever comes first
+ int end = comma;
+ if (end == -1)
+ end = header.length();
+ if (equal != -1 && end > equal)
+ end = equal; // equal sign comes before comma/end
+ QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower();
+ pos = end + 1;
+
+ if (uint(equal) < uint(comma)) {
+ // case: token "=" (token | quoted-string)
+ // skip spaces
+ pos = nextNonWhitespace(header, pos);
+ if (pos == header.length())
+ // huh? Broken header
+ return result;
+
+ QByteArray value;
+ value.reserve(header.length() - pos);
+ if (header.at(pos) == '"') {
+ // case: quoted-string
+ // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ // qdtext = <any TEXT except <">>
+ // quoted-pair = "\" CHAR
+ ++pos;
+ while (pos < header.length()) {
+ register char c = header.at(pos);
+ if (c == '"') {
+ // end of quoted text
+ break;
+ } else if (c == '\\') {
+ ++pos;
+ if (pos >= header.length())
+ // broken header
+ return result;
+ c = header.at(pos);
+ }
+
+ value += c;
+ ++pos;
+ }
+ } else {
+ // case: token
+ while (pos < header.length()) {
+ register char c = header.at(pos);
+ if (isSeparator(c))
+ break;
+ value += c;
+ ++pos;
+ }
+ }
+
+ result.insert(key, value);
+
+ // find the comma now:
+ comma = header.indexOf(',', pos);
+ if (comma == -1)
+ return result; // end of parsing
+ pos = comma + 1;
+ } else {
+ // case: token
+ // key is already set
+ result.insert(key, QByteArray());
+ }
+ }
+}
+
+QNetworkAccessBackend *
+QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const
+{
+ // check the operation
+ switch (op) {
+ case QNetworkAccessManager::GetOperation:
+ case QNetworkAccessManager::PostOperation:
+ case QNetworkAccessManager::HeadOperation:
+ case QNetworkAccessManager::PutOperation:
+ case QNetworkAccessManager::DeleteOperation:
+ case QNetworkAccessManager::CustomOperation:
+ break;
+
+ default:
+ // no, we can't handle this request
+ return 0;
+ }
+
+ QUrl url = request.url();
+ QString scheme = url.scheme().toLower();
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
+ return new QNetworkAccessHttpBackend;
+
+ return 0;
+}
+
+QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
+ : QNetworkAccessBackend()
+ , statusCode(0)
+ , pendingDownloadDataEmissions(new QAtomicInt())
+ , pendingDownloadProgressEmissions(new QAtomicInt())
+ , loadingFromCache(false)
+ , usingZerocopyDownloadBuffer(false)
+#ifndef QT_NO_OPENSSL
+ , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false)
+#endif
+ , resumeOffset(0)
+{
+}
+
+QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend()
+{
+ // This will do nothing if the request was already finished or aborted
+ emit abortHttpRequest();
+
+#ifndef QT_NO_OPENSSL
+ delete pendingSslConfiguration;
+#endif
+}
+
+/*
+ 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
+ */
+bool QNetworkAccessHttpBackend::loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest)
+{
+ QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
+ (QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
+ if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
+ // 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 false;
+
+ QAbstractNetworkCache *nc = networkCache();
+ if (!nc)
+ return false; // no local cache
+
+ QNetworkCacheMetaData metaData = nc->metaData(url());
+ if (!metaData.isValid())
+ return false; // not in cache
+
+ if (!metaData.saveToDisk())
+ return false;
+
+ QNetworkHeadersPrivate cacheHeaders;
+ QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+ cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+
+ it = cacheHeaders.findRawHeader("etag");
+ if (it != cacheHeaders.rawHeaders.constEnd())
+ httpRequest.setHeaderField("If-None-Match", it->second);
+
+ QDateTime lastModified = metaData.lastModified();
+ 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 false;
+ }
+ }
+
+ QDateTime currentDateTime = QDateTime::currentDateTime();
+ QDateTime expirationDate = metaData.expirationDate();
+
+#if 0
+ /*
+ * age_value
+ * is the value of Age: header received by the cache with
+ * this response.
+ * date_value
+ * is the value of the origin server's Date: header
+ * request_time
+ * is the (local) time when the cache made the request
+ * that resulted in this cached response
+ * response_time
+ * is the (local) time when the cache received the
+ * response
+ * now
+ * is the current (local) time
+ */
+ int age_value = 0;
+ it = cacheHeaders.findRawHeader("age");
+ if (it != cacheHeaders.rawHeaders.constEnd())
+ age_value = it->second.toInt();
+
+ QDateTime dateHeader;
+ int date_value = 0;
+ it = cacheHeaders.findRawHeader("date");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
+ date_value = dateHeader.toTime_t();
+ }
+
+ int now = currentDateTime.toUTC().toTime_t();
+ int request_time = now;
+ int response_time = now;
+
+ // Algorithm from RFC 2616 section 13.2.3
+ int apparent_age = qMax(0, response_time - date_value);
+ int corrected_received_age = qMax(apparent_age, age_value);
+ int response_delay = response_time - request_time;
+ int corrected_initial_age = corrected_received_age + response_delay;
+ int resident_time = now - response_time;
+ int current_age = corrected_initial_age + resident_time;
+
+ // RFC 2616 13.2.4 Expiration Calculations
+ if (!expirationDate.isValid()) {
+ if (lastModified.isValid()) {
+ int diff = currentDateTime.secsTo(lastModified);
+ expirationDate = lastModified;
+ expirationDate.addSecs(diff / 10);
+ if (httpRequest.headerField("Warning").isEmpty()) {
+ QDateTime dt;
+ dt.setTime_t(current_age);
+ if (dt.daysTo(currentDateTime) > 1)
+ httpRequest.setHeaderField("Warning", "113");
+ }
+ }
+ }
+
+ // the cache-saving code below sets the expirationDate with date+max_age
+ // if "max-age" is present, or to Expires otherwise
+ int freshness_lifetime = dateHeader.secsTo(expirationDate);
+ bool response_is_fresh = (freshness_lifetime > current_age);
+#else
+ bool response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0;
+#endif
+
+ if (!response_is_fresh)
+ return false;
+
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+ qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
+#endif
+ return sendCacheContents(metaData);
+}
+
+static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio)
+{
+ switch (prio) {
+ case QNetworkRequest::LowPriority:
+ return QHttpNetworkRequest::LowPriority;
+ case QNetworkRequest::HighPriority:
+ return QHttpNetworkRequest::HighPriority;
+ case QNetworkRequest::NormalPriority:
+ default:
+ return QHttpNetworkRequest::NormalPriority;
+ }
+}
+
+void QNetworkAccessHttpBackend::postRequest()
+{
+ 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;
+ httpRequest.setPriority(convert(request().priority()));
+
+ switch (operation()) {
+ case QNetworkAccessManager::GetOperation:
+ httpRequest.setOperation(QHttpNetworkRequest::Get);
+ loadedFromCache = loadFromCacheIfAllowed(httpRequest);
+ break;
+
+ case QNetworkAccessManager::HeadOperation:
+ httpRequest.setOperation(QHttpNetworkRequest::Head);
+ loadedFromCache = loadFromCacheIfAllowed(httpRequest);
+ break;
+
+ case QNetworkAccessManager::PostOperation:
+ invalidateCache();
+ httpRequest.setOperation(QHttpNetworkRequest::Post);
+ createUploadByteDevice();
+ break;
+
+ case QNetworkAccessManager::PutOperation:
+ invalidateCache();
+ httpRequest.setOperation(QHttpNetworkRequest::Put);
+ createUploadByteDevice();
+ break;
+
+ case QNetworkAccessManager::DeleteOperation:
+ invalidateCache();
+ httpRequest.setOperation(QHttpNetworkRequest::Delete);
+ break;
+
+ case QNetworkAccessManager::CustomOperation:
+ invalidateCache(); // for safety reasons, we don't know what the operation does
+ httpRequest.setOperation(QHttpNetworkRequest::Custom);
+ createUploadByteDevice();
+ httpRequest.setCustomVerb(request().attribute(
+ QNetworkRequest::CustomVerbAttribute).toByteArray());
+ break;
+
+ default:
+ break; // can't happen
+ }
+
+ 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) {
+ if (headers.contains("Range")) {
+ // Need to adjust resume offset for user specified range
+
+ headers.removeOne("Range");
+
+ // We've already verified that requestRange starts with "bytes=", see canResume.
+ QByteArray requestRange = request().rawHeader("Range").mid(6);
+
+ int index = requestRange.indexOf('-');
+
+ quint64 requestStartOffset = requestRange.left(index).toULongLong();
+ quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
+
+ requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
+ '-' + QByteArray::number(requestEndOffset);
+
+ httpRequest.setHeaderField("Range", requestRange);
+ } else {
+ httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
+ }
+ }
+
+ foreach (const QByteArray &header, headers)
+ httpRequest.setHeaderField(header, request().rawHeader(header));
+
+ if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
+ httpRequest.setPipeliningAllowed(true);
+
+ if (static_cast<QNetworkRequest::LoadControl>
+ (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,
+ QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
+ httpRequest.setWithCredentials(false);
+
+
+ // 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
+
+ // 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()));
+
+ // Set the properties it needs
+ delegate->httpRequest = httpRequest;
+#ifndef QT_NO_NETWORKPROXY
+ delegate->cacheProxy = cacheProxy;
+ delegate->transparentProxy = transparentProxy;
+#endif
+ delegate->ssl = ssl;
+#ifndef QT_NO_OPENSSL
+ if (ssl)
+ delegate->incomingSslConfiguration = request().sslConfiguration();
+#endif
+
+ // 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!
+ }
+ } 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());
+ }
+ }
+
+
+ // 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);
+ httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
+ } else {
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength);
+ replyDownloadData(delegate->synchronousDownloadData);
+ }
+
+ // End the thread. It will delete itself from the finished() signal
+ thread->quit();
+
+ finished();
+ } else {
+ emit startHttpRequest(); // Signal to the HTTP thread and go back to user.
+ }
+}
+
+void QNetworkAccessHttpBackend::invalidateCache()
+{
+ QAbstractNetworkCache *nc = networkCache();
+ if (nc)
+ nc->remove(url());
+}
+
+void QNetworkAccessHttpBackend::open()
+{
+ postRequest();
+}
+
+void QNetworkAccessHttpBackend::closeDownstreamChannel()
+{
+ // FIXME Maybe we can get rid of this whole architecture part
+}
+
+void QNetworkAccessHttpBackend::downstreamReadyWrite()
+{
+ // FIXME Maybe we can get rid of this whole architecture part
+}
+
+void QNetworkAccessHttpBackend::setDownstreamLimited(bool 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::replyDownloadData(QByteArray d)
+{
+ 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;
+ }
+
+ 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()
+{
+ // We are already loading from cache, we still however
+ // got this signal because it was posted already
+ if (loadingFromCache)
+ return;
+
+ finished();
+}
+
+void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode)
+{
+ switch (statusCode) {
+ case 301: // Moved Permanently
+ case 302: // Found
+ case 303: // See Other
+ case 307: // Temporary Redirect
+ // What do we do about the caching of the HTML note?
+ // The response to a 303 MUST NOT be cached, while the response to
+ // all of the others is cacheable if the headers indicate it to be
+ QByteArray header = rawHeader("location");
+ QUrl url = QUrl::fromEncoded(header);
+ if (!url.isValid())
+ url = QUrl(QLatin1String(header));
+ redirectionRequested(url);
+ }
+}
+
+void QNetworkAccessHttpBackend::replyDownloadMetaData
+ (QList<QPair<QByteArray,QByteArray> > hm,
+ int sc,QString rp,bool pu,
+ QSharedPointer<char> db,
+ qint64 contentLength)
+{
+ 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 = hm;
+ QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
+ end = headerMap.constEnd();
+ QByteArray header;
+
+ for (; it != end; ++it) {
+ QByteArray value = rawHeader(it->first);
+ if (!value.isEmpty()) {
+ if (qstricmp(it->first.constData(), "set-cookie") == 0)
+ value += '\n';
+ else
+ value += ", ";
+ }
+ value += it->second;
+ setRawHeader(it->first, value);
+ }
+
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
+
+ // is it a redirection?
+ checkForRedirect(statusCode);
+
+ if (statusCode >= 500 && statusCode < 600) {
+ QAbstractNetworkCache *nc = networkCache();
+ if (nc) {
+ QNetworkCacheMetaData metaData = nc->metaData(url());
+ QNetworkHeadersPrivate cacheHeaders;
+ cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+ QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+ it = cacheHeaders.findRawHeader("Cache-Control");
+ bool mustReValidate = false;
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+ if (cacheControl.contains("must-revalidate"))
+ mustReValidate = true;
+ }
+ if (!mustReValidate && sendCacheContents(metaData))
+ return;
+ }
+ }
+
+ if (statusCode == 304) {
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+ qDebug() << "Received a 304 from" << url();
+#endif
+ QAbstractNetworkCache *nc = networkCache();
+ if (nc) {
+ QNetworkCacheMetaData oldMetaData = nc->metaData(url());
+ QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData);
+ if (oldMetaData != metaData)
+ nc->updateMetaData(metaData);
+ if (sendCacheContents(metaData))
+ return;
+ }
+ }
+
+
+ if (statusCode != 304 && statusCode != 303) {
+ if (!isCachingEnabled())
+ setCachingEnabled(true);
+ }
+
+ metaDataChanged();
+}
+
+void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received, qint64 total)
+{
+ // 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::httpAuthenticationRequired(const QHttpNetworkRequest &,
+ QAuthenticator *auth)
+{
+ authenticationRequired(auth);
+}
+
+void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
+ const QString &errorString)
+{
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+ qDebug() << "http error!" << errorCode << errorString;
+#endif
+
+ error(errorCode, errorString);
+}
+
+#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());
+}
+
+/*
+ A simple web page that can be used to test us: http://www.procata.com/cachetest/
+ */
+bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &metaData)
+{
+ setCachingEnabled(false);
+ if (!metaData.isValid())
+ return false;
+
+ QAbstractNetworkCache *nc = networkCache();
+ Q_ASSERT(nc);
+ QIODevice *contents = nc->data(url());
+ if (!contents) {
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+ qDebug() << "Can not send cache, the contents are 0" << url();
+#endif
+ return false;
+ }
+ contents->setParent(this);
+
+ QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
+ int status = attributes.value(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (status < 100)
+ status = 200; // fake it
+
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
+ setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
+
+ QNetworkCacheMetaData::RawHeaderList rawHeaders = metaData.rawHeaders();
+ QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
+ end = rawHeaders.constEnd();
+ for ( ; it != end; ++it)
+ setRawHeader(it->first, it->second);
+
+ checkForRedirect(status);
+
+ // 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);
+ qRegisterMetaType<QIODevice*>("QIODevice*");
+ QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
+
+
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+ qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes";
+#endif
+
+ // Set the following flag so we can ignore some signals from HTTP thread
+ // that would still come
+ loadingFromCache = true;
+ return true;
+}
+
+void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev)
+{
+ delete dev;
+ finished();
+}
+
+#ifndef QT_NO_OPENSSL
+void QNetworkAccessHttpBackend::ignoreSslErrors()
+{
+ pendingIgnoreAllSslErrors = true;
+}
+
+void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &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 (pendingSslConfiguration)
+ config = *pendingSslConfiguration;
+ else
+ config = request().sslConfiguration();
+}
+
+void QNetworkAccessHttpBackend::setSslConfiguration(const 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
+
+QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
+{
+ QNetworkCacheMetaData metaData = oldMetaData;
+
+ QNetworkHeadersPrivate cacheHeaders;
+ cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+ QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+
+ QList<QByteArray> newHeaders = rawHeaderList();
+ foreach (QByteArray header, newHeaders) {
+ QByteArray originalHeader = header;
+ header = header.toLower();
+ bool hop_by_hop =
+ (header == "connection"
+ || header == "keep-alive"
+ || header == "proxy-authenticate"
+ || header == "proxy-authorization"
+ || header == "te"
+ || header == "trailers"
+ || header == "transfer-encoding"
+ || header == "upgrade");
+ if (hop_by_hop)
+ continue;
+
+ // we are currently not using the date header to determine the expiration time of a page,
+ // but only the "Expires", "max-age" and "s-maxage" headers, see
+ // QNetworkAccessHttpBackend::validateCache() and below ("metaData.setExpirationDate()").
+ if (header == "date")
+ continue;
+
+ // Don't store Warning 1xx headers
+ if (header == "warning") {
+ QByteArray v = rawHeader(header);
+ if (v.length() == 3
+ && v[0] == '1'
+ && v[1] >= '0' && v[1] <= '9'
+ && v[2] >= '0' && v[2] <= '9')
+ continue;
+ }
+
+ it = cacheHeaders.findRawHeader(header);
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ // Match the behavior of Firefox and assume Cache-Control: "no-transform"
+ if (header == "content-encoding"
+ || header == "content-range"
+ || header == "content-type")
+ continue;
+
+ // For MS servers that send "Content-Length: 0" on 304 responses
+ // ignore this too
+ if (header == "content-length")
+ continue;
+ }
+
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+ QByteArray n = rawHeader(header);
+ QByteArray o;
+ if (it != cacheHeaders.rawHeaders.constEnd())
+ o = (*it).second;
+ if (n != o && header != "date") {
+ qDebug() << "replacing" << header;
+ qDebug() << "new" << n;
+ qDebug() << "old" << o;
+ }
+#endif
+ cacheHeaders.setRawHeader(originalHeader, rawHeader(header));
+ }
+ metaData.setRawHeaders(cacheHeaders.rawHeaders);
+
+ bool checkExpired = true;
+
+ QHash<QByteArray, QByteArray> cacheControl;
+ it = cacheHeaders.findRawHeader("Cache-Control");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ cacheControl = parseHttpOptionHeader(it->second);
+ QByteArray maxAge = cacheControl.value("max-age");
+ if (!maxAge.isEmpty()) {
+ checkExpired = false;
+ QDateTime dt = QDateTime::currentDateTime();
+ dt = dt.addSecs(maxAge.toInt());
+ metaData.setExpirationDate(dt);
+ }
+ }
+ if (checkExpired) {
+ it = cacheHeaders.findRawHeader("expires");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second);
+ metaData.setExpirationDate(expiredDateTime);
+ }
+ }
+
+ it = cacheHeaders.findRawHeader("last-modified");
+ if (it != cacheHeaders.rawHeaders.constEnd())
+ metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second));
+
+ 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 (httpRequest.operation() == QHttpNetworkRequest::Get) {
+
+ canDiskCache = true;
+ // 14.32
+ // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client
+ // had sent "Cache-Control: no-cache".
+ it = cacheHeaders.findRawHeader("pragma");
+ if (it != cacheHeaders.rawHeaders.constEnd()
+ && it->second == "no-cache")
+ canDiskCache = false;
+
+ // HTTP/1.1. Check the Cache-Control header
+ if (cacheControl.contains("no-cache"))
+ canDiskCache = false;
+ else if (cacheControl.contains("no-store"))
+ canDiskCache = false;
+
+ // responses to POST might be cacheable
+ } else if (httpRequest.operation() == QHttpNetworkRequest::Post) {
+
+ canDiskCache = false;
+ // some pages contain "expires:" and "cache-control: no-cache" field,
+ // so we only might cache POST requests if we get "cache-control: max-age ..."
+ if (cacheControl.contains("max-age"))
+ canDiskCache = true;
+
+ // responses to PUT and DELETE are not cacheable
+ } else {
+ canDiskCache = false;
+ }
+
+ metaData.setSaveToDisk(canDiskCache);
+ QNetworkCacheMetaData::AttributesMap attributes;
+ if (statusCode != 304) {
+ // update the status code
+ attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+ attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
+ } else {
+ // this is a redirection, keep the attributes intact
+ attributes = oldMetaData.attributes();
+ }
+ metaData.setAttributes(attributes);
+ return metaData;
+}
+
+bool QNetworkAccessHttpBackend::canResume() const
+{
+ // Only GET operation supports resuming.
+ if (operation() != QNetworkAccessManager::GetOperation)
+ return false;
+
+ // Can only resume if server/resource supports Range header.
+ QByteArray acceptRangesheaderName("Accept-Ranges");
+ if (!hasRawHeader(acceptRangesheaderName) || rawHeader(acceptRangesheaderName) == "none")
+ return false;
+
+ // We only support resuming for byte ranges.
+ if (request().hasRawHeader("Range")) {
+ QByteArray range = request().rawHeader("Range");
+ if (!range.startsWith("bytes="))
+ return false;
+ }
+
+ // 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;
+}
+
+void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset)
+{
+ resumeOffset = offset;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h
new file mode 100644
index 0000000000..4778bd0c4e
--- /dev/null
+++ b/src/network/access/qnetworkaccesshttpbackend_p.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSHTTPBACKEND_P_H
+#define QNETWORKACCESSHTTPBACKEND_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 "qhttpnetworkconnection_p.h"
+#include "qnetworkaccessbackend_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "qabstractsocket.h"
+
+#include "QtCore/qpointer.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qsharedpointer.h"
+#include "qatomic.h"
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAccessCachedHttpConnection;
+
+class QNetworkAccessHttpBackendIODevice;
+
+class QNetworkAccessHttpBackend: public QNetworkAccessBackend
+{
+ Q_OBJECT
+public:
+ QNetworkAccessHttpBackend();
+ virtual ~QNetworkAccessHttpBackend();
+
+ virtual void open();
+ virtual void closeDownstreamChannel();
+
+ virtual void downstreamReadyWrite();
+ virtual void setDownstreamLimited(bool b);
+
+ virtual void copyFinished(QIODevice *);
+#ifndef QT_NO_OPENSSL
+ virtual void ignoreSslErrors();
+ virtual void ignoreSslErrors(const QList<QSslError> &errors);
+
+ virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
+ virtual void setSslConfiguration(const QSslConfiguration &configuration);
+#endif
+ QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
+
+ // we return true since HTTP needs to send PUT/POST data again after having authenticated
+ bool needsResetableUploadData() { return true; }
+
+ bool canResume() const;
+ void setResumeOffset(quint64 offset);
+
+signals:
+ // To HTTP thread:
+ void startHttpRequest();
+ void abortHttpRequest();
+
+ void startHttpRequestSynchronously();
+
+ void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
+private slots:
+ // From HTTP thread:
+ void replyDownloadData(QByteArray);
+ void replyFinished();
+ void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+ void replyDownloadProgressSlot(qint64,qint64);
+ void httpAuthenticationRequired(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);
+
+private:
+ 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;
+ bool pendingIgnoreAllSslErrors;
+ QList<QSslError> pendingIgnoreSslErrorsList;
+#endif
+
+ quint64 resumeOffset;
+
+ bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
+ void invalidateCache();
+ void postRequest();
+ void readFromHttp();
+ void checkForRedirect(const int statusCode);
+};
+
+class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory
+{
+public:
+ virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &request) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
new file mode 100644
index 0000000000..5a7521e33e
--- /dev/null
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -0,0 +1,1299 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkaccessmanager.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkcookie.h"
+#include "qabstractnetworkcache.h"
+
+#include "QtNetwork/qnetworksession.h"
+#include "QtNetwork/private/qsharednetworksession_p.h"
+
+#include "qnetworkaccesshttpbackend_p.h"
+#include "qnetworkaccessftpbackend_p.h"
+#include "qnetworkaccessfilebackend_p.h"
+#include "qnetworkaccessdebugpipebackend_p.h"
+#include "qnetworkaccesscachebackend_p.h"
+#include "qnetworkreplydataimpl_p.h"
+#include "qnetworkreplyfileimpl_p.h"
+
+#include "QtCore/qbuffer.h"
+#include "QtCore/qurl.h"
+#include "QtCore/qvector.h"
+#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
+
+#ifndef QT_NO_HTTP
+Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
+#endif // QT_NO_HTTP
+Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
+#ifndef QT_NO_FTP
+Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
+#endif // QT_NO_FTP
+
+#ifdef QT_BUILD_INTERNAL
+Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
+#endif
+
+static void ensureInitialized()
+{
+#ifndef QT_NO_HTTP
+ (void) httpBackend();
+#endif // QT_NO_HTTP
+
+#ifndef QT_NO_FTP
+ (void) ftpBackend();
+#endif
+
+#ifdef QT_BUILD_INTERNAL
+ (void) debugpipeBackend();
+#endif
+
+ // leave this one last since it will query the special QAbstractFileEngines
+ (void) fileBackend();
+}
+
+/*!
+ \class QNetworkAccessManager
+ \brief The QNetworkAccessManager class allows the application to
+ send network requests and receive replies
+ \since 4.4
+
+ \ingroup network
+ \inmodule QtNetwork
+ \reentrant
+
+ The Network Access API is constructed around one QNetworkAccessManager
+ object, which holds the common configuration and settings for the requests
+ it sends. It contains the proxy and cache configuration, as well as the
+ signals related to such issues, and reply signals that can be used to
+ monitor the progress of a network operation. One QNetworkAccessManager
+ should be enough for the whole Qt application.
+
+ Once a QNetworkAccessManager object has been created, the application can
+ use it to send requests over the network. A group of standard functions
+ are supplied that take a request and optional data, and each return a
+ QNetworkReply object. The returned object is used to obtain any data
+ returned in response to the corresponding request.
+
+ A simple download off the network could be accomplished with:
+ \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 0
+
+ QNetworkAccessManager has an asynchronous API.
+ When the \tt replyFinished slot above is called, the parameter it
+ takes is the QNetworkReply object containing the downloaded data
+ as well as meta-data (headers, etc.).
+
+ \note After the request has finished, it is the responsibility of the user
+ to delete the QNetworkReply object at an appropriate time. Do not directly
+ delete it inside the slot connected to finished(). You can use the
+ deleteLater() function.
+
+ \note QNetworkAccessManager queues the requests it receives. The number
+ of requests executed in parallel is dependent on the protocol.
+ Currently, for the HTTP protocol on desktop platforms, 6 requests are
+ executed in parallel for one host/port combination.
+
+ A more involved example, assuming the manager is already existent,
+ can be:
+ \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 1
+
+ \section1 Network and Roaming support
+
+ With the addition of the \l {Bearer Management} API to Qt 4.7
+ QNetworkAccessManager gained the ability to manage network connections.
+ QNetworkAccessManager can start the network interface if the device is
+ offline and terminates the interface if the current process is the last
+ one to use the uplink. Note that some platform utilize grace periods from
+ when the last application stops using a uplink until the system actually
+ terminates the connectivity link. Roaming is equally transparent. Any
+ queued/pending network requests are automatically transferred to new
+ access point.
+
+ Clients wanting to utilize this feature should not require any changes. In fact
+ it is likely that existing platform specific connection code can simply be
+ removed from the application.
+
+ \note The network and roaming support in QNetworkAccessManager is conditional
+ upon the platform supporting connection management. The
+ \l QNetworkConfigurationManager::NetworkSessionRequired can be used to
+ detect whether QNetworkAccessManager utilizes this feature. Currently only
+ Meego/Harmattan and Symbian platforms provide connection management support.
+
+ \note This feature cannot be used in combination with the Bearer Management
+ API as provided by QtMobility. Applications have to migrate to the Qt version
+ of Bearer Management.
+
+ \section1 Symbian Platform Security Requirements
+
+ On Symbian, processes which use this class must have the
+ \c NetworkServices platform security capability. If the client
+ process lacks this capability, operations will result in a panic.
+
+ Platform security capabilities are added via the
+ \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
+ qmake variable.
+
+ \sa QNetworkRequest, QNetworkReply, QNetworkProxy
+*/
+
+/*!
+ \enum QNetworkAccessManager::Operation
+
+ Indicates the operation this reply is processing.
+
+ \value HeadOperation retrieve headers operation (created
+ with head())
+
+ \value GetOperation retrieve headers and download contents
+ (created with get())
+
+ \value PutOperation upload contents operation (created
+ with put())
+
+ \value PostOperation send the contents of an HTML form for
+ processing via HTTP POST (created with post())
+
+ \value DeleteOperation delete contents operation (created with
+ deleteResource())
+
+ \value CustomOperation custom operation (created with
+ sendCustomRequest()) \since 4.7
+
+ \omitvalue UnknownOperation
+
+ \sa QNetworkReply::operation()
+*/
+
+/*!
+ \enum QNetworkAccessManager::NetworkAccessibility
+
+ Indicates whether the network is accessible via this network access manager.
+
+ \value UnknownAccessibility The network accessibility cannot be determined.
+ \value NotAccessible The network is not currently accessible, either because there
+ is currently no network coverage or network access has been
+ explicitly disabled by a call to setNetworkAccessible().
+ \value Accessible The network is accessible.
+
+ \sa networkAccessible
+*/
+
+/*!
+ \property QNetworkAccessManager::networkAccessible
+ \brief whether the network is currently accessible via this network access manager.
+
+ \since 4.7
+
+ If the network is \l {NotAccessible}{not accessible} the network access manager will not
+ process any new network requests, all such requests will fail with an error. Requests with
+ URLs with the file:// scheme will still be processed.
+
+ By default the value of this property reflects the physical state of the device. Applications
+ may override it to disable all network requests via this network access manager by calling
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 4
+
+ Network requests can be reenabled again by calling
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 5
+
+ \note Calling setNetworkAccessible() does not change the network state.
+*/
+
+/*!
+ \fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
+
+ This signal is emitted when the value of the \l networkAccessible property changes.
+ \a accessible is the new network accessibility.
+*/
+
+/*!
+ \fn void QNetworkAccessManager::networkSessionConnected()
+
+ \since 4.7
+
+ \internal
+
+ This signal is emitted when the status of the network session changes into a usable (Connected)
+ state. It is used to signal to QNetworkReplys to start or migrate their network operation once
+ the network session has been opened or finished roaming.
+*/
+
+/*!
+ \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
+
+ This signal is emitted whenever a proxy requests authentication
+ and QNetworkAccessManager cannot find a valid, cached
+ credential. The slot connected to this signal should fill in the
+ credentials for the proxy \a proxy in the \a authenticator object.
+
+ QNetworkAccessManager will cache the credentials internally. The
+ next time the proxy requests authentication, QNetworkAccessManager
+ will automatically send the same credential without emitting the
+ proxyAuthenticationRequired signal again.
+
+ If the proxy rejects the credentials, QNetworkAccessManager will
+ emit the signal again.
+
+ \sa proxy(), setProxy(), authenticationRequired()
+*/
+
+/*!
+ \fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
+
+ This signal is emitted whenever a final server requests
+ authentication before it delivers the requested contents. The slot
+ connected to this signal should fill the credentials for the
+ contents (which can be determined by inspecting the \a reply
+ object) in the \a authenticator object.
+
+ QNetworkAccessManager will cache the credentials internally and
+ will send the same values if the server requires authentication
+ again, without emitting the authenticationRequired() signal. If it
+ rejects the credentials, this signal will be emitted again.
+
+ \sa proxyAuthenticationRequired()
+*/
+
+/*!
+ \fn void QNetworkAccessManager::finished(QNetworkReply *reply)
+
+ This signal is emitted whenever a pending network reply is
+ finished. The \a reply parameter will contain a pointer to the
+ reply that has just finished. This signal is emitted in tandem
+ with the QNetworkReply::finished() signal.
+
+ See QNetworkReply::finished() for information on the status that
+ the object will be in.
+
+ \note Do not delete the \a reply object in the slot connected to this
+ signal. Use deleteLater().
+
+ \sa QNetworkReply::finished(), QNetworkReply::error()
+*/
+
+/*!
+ \fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
+
+ This signal is emitted if the SSL/TLS session encountered errors
+ during the set up, including certificate verification errors. The
+ \a errors parameter contains the list of errors and \a reply is
+ the QNetworkReply that is encountering these errors.
+
+ To indicate that the errors are not fatal and that the connection
+ should proceed, the QNetworkReply::ignoreSslErrors() function should be called
+ from the slot connected to this signal. If it is not called, the
+ SSL session will be torn down before any data is exchanged
+ (including the URL).
+
+ This signal can be used to display an error message to the user
+ indicating that security may be compromised and display the
+ SSL settings (see sslConfiguration() to obtain it). If the user
+ decides to proceed after analyzing the remote certificate, the
+ slot should call ignoreSslErrors().
+
+ \sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(),
+ QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
+*/
+
+
+/*!
+ Constructs a QNetworkAccessManager object that is the center of
+ the Network Access API and sets \a parent as the parent object.
+*/
+QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
+ : QObject(*new QNetworkAccessManagerPrivate, parent)
+{
+ ensureInitialized();
+
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
+}
+
+/*!
+ Destroys the QNetworkAccessManager object and frees up any
+ resources. Note that QNetworkReply objects that are returned from
+ this class have this object set as their parents, which means that
+ they will be deleted along with it if you don't call
+ QObject::setParent() on them.
+*/
+QNetworkAccessManager::~QNetworkAccessManager()
+{
+#ifndef QT_NO_NETWORKPROXY
+ delete d_func()->proxyFactory;
+#endif
+
+ // Delete the QNetworkReply children first.
+ // Else a QAbstractNetworkCache might get deleted in ~QObject
+ // before a QNetworkReply that accesses the QAbstractNetworkCache
+ // object in its destructor.
+ qDeleteAll(findChildren<QNetworkReply *>());
+ // The other children will be deleted in this ~QObject
+ // FIXME instead of this "hack" make the QNetworkReplyImpl
+ // properly watch the cache deletion, e.g. via a QWeakPointer.
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*!
+ Returns the QNetworkProxy that the requests sent using this
+ QNetworkAccessManager object will use. The default value for the
+ proxy is QNetworkProxy::DefaultProxy.
+
+ \sa setProxy(), setProxyFactory(), proxyAuthenticationRequired()
+*/
+QNetworkProxy QNetworkAccessManager::proxy() const
+{
+ return d_func()->proxy;
+}
+
+/*!
+ Sets the proxy to be used in future requests to be \a proxy. This
+ does not affect requests that have already been sent. The
+ proxyAuthenticationRequired() signal will be emitted if the proxy
+ requests authentication.
+
+ A proxy set with this function will be used for all requests
+ issued by QNetworkAccessManager. In some cases, it might be
+ necessary to select different proxies depending on the type of
+ request being sent or the destination host. If that's the case,
+ you should consider using setProxyFactory().
+
+ \sa proxy(), proxyAuthenticationRequired()
+*/
+void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy)
+{
+ Q_D(QNetworkAccessManager);
+ delete d->proxyFactory;
+ d->proxy = proxy;
+ d->proxyFactory = 0;
+}
+
+/*!
+ \fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
+ \since 4.5
+
+ Returns the proxy factory that this QNetworkAccessManager object
+ is using to determine the proxies to be used for requests.
+
+ Note that the pointer returned by this function is managed by
+ QNetworkAccessManager and could be deleted at any time.
+
+ \sa setProxyFactory(), proxy()
+*/
+QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
+{
+ return d_func()->proxyFactory;
+}
+
+/*!
+ \since 4.5
+
+ Sets the proxy factory for this class to be \a factory. A proxy
+ factory is used to determine a more specific list of proxies to be
+ used for a given request, instead of trying to use the same proxy
+ value for all requests.
+
+ All queries sent by QNetworkAccessManager will have type
+ QNetworkProxyQuery::UrlRequest.
+
+ For example, a proxy factory could apply the following rules:
+ \list
+ \o if the target address is in the local network (for example,
+ if the hostname contains no dots or if it's an IP address in
+ the organization's range), return QNetworkProxy::NoProxy
+ \o if the request is FTP, return an FTP proxy
+ \o if the request is HTTP or HTTPS, then return an HTTP proxy
+ \o otherwise, return a SOCKSv5 proxy server
+ \endlist
+
+ The lifetime of the object \a factory will be managed by
+ QNetworkAccessManager. It will delete the object when necessary.
+
+ \note If a specific proxy is set with setProxy(), the factory will not
+ be used.
+
+ \sa proxyFactory(), setProxy(), QNetworkProxyQuery
+*/
+void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory)
+{
+ Q_D(QNetworkAccessManager);
+ delete d->proxyFactory;
+ d->proxyFactory = factory;
+ d->proxy = QNetworkProxy();
+}
+#endif
+
+/*!
+ \since 4.5
+
+ Returns the cache that is used to store data obtained from the network.
+
+ \sa setCache()
+*/
+QAbstractNetworkCache *QNetworkAccessManager::cache() const
+{
+ Q_D(const QNetworkAccessManager);
+ return d->networkCache;
+}
+
+/*!
+ \since 4.5
+
+ Sets the manager's network cache to be the \a cache specified. The cache
+ is used for all requests dispatched by the manager.
+
+ Use this function to set the network cache object to a class that implements
+ additional features, like saving the cookies to permanent storage.
+
+ \note QNetworkAccessManager takes ownership of the \a cache object.
+
+ QNetworkAccessManager by default does not have a set cache.
+ Qt provides a simple disk cache, QNetworkDiskCache, which can be used.
+
+ \sa cache(), QNetworkRequest::CacheLoadControl
+*/
+void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache)
+{
+ Q_D(QNetworkAccessManager);
+ if (d->networkCache != cache) {
+ delete d->networkCache;
+ d->networkCache = cache;
+ if (d->networkCache)
+ d->networkCache->setParent(this);
+ }
+}
+
+/*!
+ Returns the QNetworkCookieJar that is used to store cookies
+ obtained from the network as well as cookies that are about to be
+ sent.
+
+ \sa setCookieJar()
+*/
+QNetworkCookieJar *QNetworkAccessManager::cookieJar() const
+{
+ Q_D(const QNetworkAccessManager);
+ if (!d->cookieJar)
+ d->createCookieJar();
+ return d->cookieJar;
+}
+
+/*!
+ Sets the manager's cookie jar to be the \a cookieJar specified.
+ The cookie jar is used by all requests dispatched by the manager.
+
+ Use this function to set the cookie jar object to a class that
+ implements additional features, like saving the cookies to permanent
+ storage.
+
+ \note QNetworkAccessManager takes ownership of the \a cookieJar object.
+
+ If \a cookieJar is in the same thread as this QNetworkAccessManager,
+ it will set the parent of the \a cookieJar
+ so that the cookie jar is deleted when this
+ object is deleted as well. If you want to share cookie jars
+ between different QNetworkAccessManager objects, you may want to
+ set the cookie jar's parent to 0 after calling this function.
+
+ QNetworkAccessManager by default does not implement any cookie
+ policy of its own: it accepts all cookies sent by the server, as
+ long as they are well formed and meet the minimum security
+ requirements (cookie domain matches the request's and cookie path
+ matches the request's). In order to implement your own security
+ policy, override the QNetworkCookieJar::cookiesForUrl() and
+ QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those
+ functions are called by QNetworkAccessManager when it detects a
+ new cookie.
+
+ \sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl()
+*/
+void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
+{
+ Q_D(QNetworkAccessManager);
+ d->cookieJarCreated = true;
+ if (d->cookieJar != cookieJar) {
+ if (d->cookieJar && d->cookieJar->parent() == this)
+ delete d->cookieJar;
+ d->cookieJar = cookieJar;
+ if (thread() == cookieJar->thread())
+ d->cookieJar->setParent(this);
+ }
+}
+
+/*!
+ Posts a request to obtain the network headers for \a request
+ and returns a new QNetworkReply object which will contain such headers.
+
+ The function is named after the HTTP request associated (HEAD).
+*/
+QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
+{
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::HeadOperation, request));
+}
+
+/*!
+ Posts a request to obtain the contents of the target \a request
+ and returns a new QNetworkReply object opened for reading which emits the
+ \l{QIODevice::readyRead()}{readyRead()} signal whenever new data
+ arrives.
+
+ The contents as well as associated headers will be downloaded.
+
+ \sa post(), put(), deleteResource(), sendCustomRequest()
+*/
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
+{
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));
+}
+
+/*!
+ Sends an HTTP POST request to the destination specified by \a request
+ and returns a new QNetworkReply object opened for reading that will
+ contain the reply sent by the server. The contents of the \a data
+ device will be uploaded to the server.
+
+ \a data must be open for reading and must remain valid until the
+ finished() signal is emitted for this reply.
+
+ \note Sending a POST request on protocols other than HTTP and
+ HTTPS is undefined and will probably fail.
+
+ \sa get(), put(), deleteResource(), sendCustomRequest()
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
+{
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data));
+}
+
+/*!
+ \overload
+
+ Sends the contents of the \a data byte array to the destination
+ specified by \a request.
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = post(request, buffer);
+ buffer->setParent(reply);
+ 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, 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.
+
+ \a data must be opened for reading when this function is called
+ and must remain valid until the finished() signal is emitted for
+ this reply.
+
+ Whether anything will be available for reading from the returned
+ object is protocol dependent. For HTTP, the server may send a
+ small HTML page indicating the upload was successful (or not).
+ Other protocols will probably have content in their replies.
+
+ \note For HTTP, this request will send a PUT request, which most servers
+ do not allow. Form upload mechanisms, including that of uploading
+ files through HTML forms, use the POST mechanism.
+
+ \sa get(), post(), deleteResource(), sendCustomRequest()
+*/
+QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
+{
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data));
+}
+
+/*!
+ \overload
+
+ 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)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = put(request, buffer);
+ buffer->setParent(reply);
+ return reply;
+}
+
+/*!
+ \since 4.6
+
+ Sends a request to delete the resource identified by the URL of \a request.
+
+ \note This feature is currently available for HTTP only, performing an
+ HTTP DELETE request.
+
+ \sa get(), post(), put(), sendCustomRequest()
+*/
+QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
+{
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request));
+}
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+/*!
+ \since 4.7
+
+ Sets the network configuration that will be used when creating the
+ \l {QNetworkSession}{network session} to \a config.
+
+ The network configuration is used to create and open a network session before any request that
+ requires network access is process. If no network configuration is explicitly set via this
+ function the network configuration returned by
+ QNetworkConfigurationManager::defaultConfiguration() will be used.
+
+ To restore the default network configuration set the network configuration to the value
+ returned from QNetworkConfigurationManager::defaultConfiguration().
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 2
+
+ If an invalid network configuration is set, a network session will not be created. In this
+ case network requests will be processed regardless, but may fail. For example:
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 3
+
+ \sa configuration(), QNetworkSession
+*/
+void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config)
+{
+ d_func()->createSession(config);
+}
+
+/*!
+ \since 4.7
+
+ Returns the network configuration that will be used to create the
+ \l {QNetworkSession}{network session} which will be used when processing network requests.
+
+ \sa setConfiguration(), activeConfiguration()
+*/
+QNetworkConfiguration QNetworkAccessManager::configuration() const
+{
+ Q_D(const QNetworkAccessManager);
+
+ if (d->networkSession)
+ return d->networkSession->configuration();
+ else
+ return QNetworkConfiguration();
+}
+
+/*!
+ \since 4.7
+
+ Returns the current active network configuration.
+
+ If the network configuration returned by configuration() is of type
+ QNetworkConfiguration::ServiceNetwork this function will return the current active child
+ network configuration of that configuration. Otherwise returns the same network configuration
+ as configuration().
+
+ Use this function to return the actual network configuration currently in use by the network
+ session.
+
+ \sa configuration()
+*/
+QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
+{
+ Q_D(const QNetworkAccessManager);
+
+ if (d->networkSession) {
+ QNetworkConfigurationManager manager;
+
+ return manager.configurationFromIdentifier(
+ d->networkSession->sessionProperty(QLatin1String("ActiveConfiguration")).toString());
+ } else {
+ return QNetworkConfiguration();
+ }
+}
+
+/*!
+ \since 4.7
+
+ Overrides the reported network accessibility. If \a accessible is NotAccessible the reported
+ network accessiblity will always be NotAccessible. Otherwise the reported network
+ accessibility will reflect the actual device state.
+*/
+void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
+{
+ Q_D(QNetworkAccessManager);
+
+ if (d->networkAccessible != accessible) {
+ NetworkAccessibility previous = networkAccessible();
+ d->networkAccessible = accessible;
+ NetworkAccessibility current = networkAccessible();
+ if (previous != current)
+ emit networkAccessibleChanged(current);
+ }
+}
+
+/*!
+ \since 4.7
+
+ Returns the current network accessibility.
+*/
+QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
+{
+ Q_D(const QNetworkAccessManager);
+
+ if (d->networkSession) {
+ // d->online holds online/offline state of this network session.
+ if (d->online)
+ return d->networkAccessible;
+ else
+ return NotAccessible;
+ } else {
+ // Network accessibility is either disabled or unknown.
+ return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility;
+ }
+}
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+/*!
+ \since 4.7
+
+ Sends a custom request to the server identified by the URL of \a request.
+
+ It is the user's responsibility to send a \a verb to the server that is valid
+ according to the HTTP specification.
+
+ This method provides means to send verbs other than the common ones provided
+ via get() or post() etc., for instance sending an HTTP OPTIONS command.
+
+ If \a data is not empty, the contents of the \a data
+ device will be uploaded to the server; in that case, data must be open for
+ reading and must remain valid until the finished() signal is emitted for this reply.
+
+ \note This feature is currently available for HTTP only.
+
+ \sa get(), post(), put(), deleteResource()
+*/
+QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data)
+{
+ QNetworkRequest newRequest(request);
+ newRequest.setAttribute(QNetworkRequest::CustomVerbAttribute, verb);
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::CustomOperation, newRequest, data));
+}
+
+/*!
+ Returns a new QNetworkReply object to handle the operation \a op
+ and request \a req. The device \a outgoingData is always 0 for Get and
+ Head requests, but is the value passed to post() and put() in
+ those operations (the QByteArray variants will pass a QBuffer
+ object).
+
+ The default implementation calls QNetworkCookieJar::cookiesForUrl()
+ on the cookie jar set with setCookieJar() to obtain the cookies to
+ be sent to the remote server.
+
+ The returned object must be in an open state.
+*/
+QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
+ const QNetworkRequest &req,
+ QIODevice *outgoingData)
+{
+ Q_D(QNetworkAccessManager);
+
+ bool isLocalFile = req.url().isLocalFile();
+ QString scheme = req.url().scheme().toLower();
+
+ // fast path for GET on file:// URLs
+ // The QNetworkAccessFileBackend will right now only be used for PUT
+ if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
+ && (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
+ QNetworkRequest::CacheLoadControl mode =
+ static_cast<QNetworkRequest::CacheLoadControl>(
+ req.attribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferNetwork).toInt());
+ if (mode == QNetworkRequest::AlwaysCache
+ && (op == QNetworkAccessManager::GetOperation
+ || op == QNetworkAccessManager::HeadOperation)) {
+ // FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106
+ QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
+ QNetworkReplyImplPrivate *priv = reply->d_func();
+ priv->manager = this;
+ priv->backend = new QNetworkAccessCacheBackend();
+ priv->backend->manager = this->d_func();
+ priv->backend->setParent(reply);
+ priv->backend->reply = priv;
+ priv->setup(op, req, outgoingData);
+ return reply;
+ }
+
+#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 && !isLocalFile) {
+ return new QDisabledNetworkReply(this, req, op);
+ }
+
+ if (!d->networkSession && (d->initializeSession || !d->networkConfiguration.isEmpty())) {
+ QNetworkConfigurationManager manager;
+ if (!d->networkConfiguration.isEmpty()) {
+ d->createSession(manager.configurationFromIdentifier(d->networkConfiguration));
+ } else {
+ if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired)
+ d->createSession(manager.defaultConfiguration());
+ else
+ d->initializeSession = false;
+ }
+ }
+
+ if (d->networkSession)
+ d->networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), -1);
+#endif
+
+ QNetworkRequest request = req;
+ if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
+ outgoingData && !outgoingData->isSequential()) {
+ // request has no Content-Length
+ // but the data that is outgoing is random-access
+ request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
+ }
+
+ if (static_cast<QNetworkRequest::LoadControl>
+ (request.attribute(QNetworkRequest::CookieLoadControlAttribute,
+ QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
+ if (d->cookieJar) {
+ QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
+ if (!cookies.isEmpty())
+ request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
+ }
+ }
+
+ // first step: create the reply
+ QUrl url = request.url();
+ QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
+#ifndef QT_NO_BEARERMANAGEMENT
+ if (!isLocalFile) {
+ connect(this, SIGNAL(networkSessionConnected()),
+ reply, SLOT(_q_networkSessionConnected()));
+ }
+#endif
+ QNetworkReplyImplPrivate *priv = reply->d_func();
+ priv->manager = this;
+
+ // second step: fetch cached credentials
+ // This is not done for the time being, we should use signal emissions to request
+ // the credentials from cache.
+
+ // 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;
+ }
+
+#ifndef QT_NO_OPENSSL
+ reply->setSslConfiguration(request.sslConfiguration());
+#endif
+
+ // fourth step: setup the reply
+ priv->setup(op, request, outgoingData);
+
+ return reply;
+}
+
+void QNetworkAccessManagerPrivate::_q_replyFinished()
+{
+ Q_Q(QNetworkAccessManager);
+
+ QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
+ if (reply)
+ emit q->finished(reply);
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ if (networkSession && q->findChildren<QNetworkReply *>().count() == 1)
+ networkSession->setSessionProperty(QLatin1String("AutoCloseSessionTimeout"), 120000);
+#endif
+}
+
+void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors)
+{
+#ifndef QT_NO_OPENSSL
+ Q_Q(QNetworkAccessManager);
+ QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
+ if (reply)
+ emit q->sslErrors(reply, errors);
+#else
+ Q_UNUSED(errors);
+#endif
+}
+
+QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
+{
+ Q_Q(QNetworkAccessManager);
+ QNetworkReplyPrivate::setManager(reply, q);
+ q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished()));
+#ifndef QT_NO_OPENSSL
+ /* In case we're compiled without SSL support, we don't have this signal and we need to
+ * avoid getting a connection error. */
+ q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
+#endif
+
+ return reply;
+}
+
+void QNetworkAccessManagerPrivate::createCookieJar() const
+{
+ if (!cookieJarCreated) {
+ // keep the ugly hack in here
+ QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this);
+ that->cookieJar = new QNetworkCookieJar(that->q_func());
+ that->cookieJarCreated = true;
+ }
+}
+
+void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend *backend,
+ QAuthenticator *authenticator)
+{
+ Q_Q(QNetworkAccessManager);
+
+ // FIXME: Add support for domains (i.e., the leading path)
+ QUrl url = backend->reply->url;
+
+ // don't try the cache for the same URL twice in a row
+ // being called twice for the same URL means the authentication failed
+ // also called when last URL is empty, e.g. on first call
+ if (backend->reply->urlForLastAuthentication.isEmpty()
+ || url != backend->reply->urlForLastAuthentication) {
+ QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator);
+ if (!cred.isNull()) {
+ authenticator->setUser(cred.user);
+ authenticator->setPassword(cred.password);
+ backend->reply->urlForLastAuthentication = url;
+ return;
+ }
+ }
+
+ // if we emit a signal here in synchronous mode, the user might spin
+ // an event loop, which might recurse and lead to problems
+ if (backend->isSynchronous())
+ return;
+
+ backend->reply->urlForLastAuthentication = url;
+ emit q->authenticationRequired(backend->reply->q_func(), authenticator);
+ authenticationManager->cacheCredentials(url, authenticator);
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend,
+ const QNetworkProxy &proxy,
+ QAuthenticator *authenticator)
+{
+ Q_Q(QNetworkAccessManager);
+ // ### FIXME Tracking of successful authentications
+ // This code is a bit broken right now for SOCKS authentication
+ // first request: proxyAuthenticationRequired gets emitted, credentials gets saved
+ // second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true,
+ // proxyAuthenticationRequired gets emitted again
+ // possible solution: some tracking inside the authenticator
+ // or a new function proxyAuthenticationSucceeded(true|false)
+ if (proxy != backend->reply->lastProxyAuthentication) {
+ QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
+ if (!cred.isNull()) {
+ authenticator->setUser(cred.user);
+ authenticator->setPassword(cred.password);
+ return;
+ }
+ }
+
+ // if we emit a signal here in synchronous mode, the user might spin
+ // an event loop, which might recurse and lead to problems
+ if (backend->isSynchronous())
+ return;
+
+ backend->reply->lastProxyAuthentication = proxy;
+ emit q->proxyAuthenticationRequired(proxy, authenticator);
+ authenticationManager->cacheProxyCredentials(proxy, authenticator);
+}
+
+QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
+{
+ QList<QNetworkProxy> proxies;
+ if (proxyFactory) {
+ proxies = proxyFactory->queryProxy(query);
+ if (proxies.isEmpty()) {
+ qWarning("QNetworkAccessManager: factory %p has returned an empty result set",
+ proxyFactory);
+ proxies << QNetworkProxy::NoProxy;
+ }
+ } else if (proxy.type() == QNetworkProxy::DefaultProxy) {
+ // no proxy set, query the application
+ return QNetworkProxyFactory::proxyForQuery(query);
+ } else {
+ proxies << proxy;
+ }
+
+ return proxies;
+}
+#endif
+
+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
+void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config)
+{
+ Q_Q(QNetworkAccessManager);
+
+ initializeSession = false;
+
+ 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)
+ emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
+ else
+ emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility);
+
+ return;
+ }
+
+ //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);
+ QObject::connect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)),
+ q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection);
+
+ _q_networkSessionStateChanged(networkSession->state());
+}
+
+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();
+ }
+}
+
+void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state)
+{
+ Q_Q(QNetworkAccessManager);
+
+ //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;
+ emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
+ }
+ } else {
+ if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
+ online = true;
+ emit q->networkAccessibleChanged(networkAccessible);
+ }
+ }
+}
+#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
new file mode 100644
index 0000000000..47760b210b
--- /dev/null
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSMANAGER_H
+#define QNETWORKACCESSMANAGER_H
+
+#include <QtCore/QObject>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QIODevice;
+class QAbstractNetworkCache;
+class QAuthenticator;
+class QByteArray;
+template<typename T> class QList;
+class QNetworkCookie;
+class QNetworkCookieJar;
+class QNetworkRequest;
+class QNetworkReply;
+class QNetworkProxy;
+class QNetworkProxyFactory;
+class QSslError;
+#if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER)
+class QNetworkConfiguration;
+#endif
+class QHttpMultiPart;
+
+class QNetworkReplyImplPrivate;
+class QNetworkAccessManagerPrivate;
+class Q_NETWORK_EXPORT QNetworkAccessManager: public QObject
+{
+ Q_OBJECT
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ Q_PROPERTY(NetworkAccessibility networkAccessible READ networkAccessible WRITE setNetworkAccessible NOTIFY networkAccessibleChanged)
+#endif
+
+public:
+ enum Operation {
+ HeadOperation = 1,
+ GetOperation,
+ PutOperation,
+ PostOperation,
+ DeleteOperation,
+ CustomOperation,
+
+ UnknownOperation = 0
+ };
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ enum NetworkAccessibility {
+ UnknownAccessibility = -1,
+ NotAccessible = 0,
+ Accessible = 1
+ };
+#endif
+
+ explicit QNetworkAccessManager(QObject *parent = 0);
+ ~QNetworkAccessManager();
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy() const;
+ void setProxy(const QNetworkProxy &proxy);
+ QNetworkProxyFactory *proxyFactory() const;
+ void setProxyFactory(QNetworkProxyFactory *factory);
+#endif
+
+ QAbstractNetworkCache *cache() const;
+ void setCache(QAbstractNetworkCache *cache);
+
+ QNetworkCookieJar *cookieJar() const;
+ void setCookieJar(QNetworkCookieJar *cookieJar);
+
+ QNetworkReply *head(const QNetworkRequest &request);
+ 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);
+
+#if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER)
+ void setConfiguration(const QNetworkConfiguration &config);
+ QNetworkConfiguration configuration() const;
+ QNetworkConfiguration activeConfiguration() const;
+#endif
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ void setNetworkAccessible(NetworkAccessibility accessible);
+ NetworkAccessibility networkAccessible() const;
+#endif
+
+Q_SIGNALS:
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
+#endif
+ void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
+ void finished(QNetworkReply *reply);
+#ifndef QT_NO_OPENSSL
+ void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
+#endif
+
+#if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER)
+ void networkSessionConnected();
+#endif
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ void networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
+#endif
+
+protected:
+ virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
+ QIODevice *outgoingData = 0);
+
+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>))
+#if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER)
+ Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed())
+ Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State))
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
new file mode 100644
index 0000000000..f64cc4dc79
--- /dev/null
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKACCESSMANAGER_P_H
+#define QNETWORKACCESSMANAGER_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 "private/qobject_p.h"
+#include "QtNetwork/qnetworkproxy.h"
+#include "QtNetwork/qnetworksession.h"
+#include "qnetworkaccessauthenticationmanager_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QAbstractNetworkCache;
+class QNetworkAuthenticationCredential;
+class QNetworkCookieJar;
+
+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),
+ authenticationManager(new QNetworkAccessAuthenticationManager)
+ { }
+ ~QNetworkAccessManagerPrivate();
+
+ void _q_replyFinished();
+ void _q_replySslErrors(const QList<QSslError> &errors);
+ QNetworkReply *postProcess(QNetworkReply *reply);
+ void createCookieJar() const;
+
+ void authenticationRequired(QNetworkAccessBackend *backend, QAuthenticator *authenticator);
+ void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
+ QNetworkAuthenticationCredential *fetchCachedCredentials(const QUrl &url,
+ const QAuthenticator *auth = 0);
+
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(QNetworkAccessBackend *backend, const QNetworkProxy &proxy,
+ QAuthenticator *authenticator);
+ void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
+ QNetworkAuthenticationCredential *fetchCachedProxyCredentials(const QNetworkProxy &proxy,
+ const QAuthenticator *auth = 0);
+ QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query);
+#endif
+
+ QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request);
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ void createSession(const QNetworkConfiguration &config);
+
+ void _q_networkSessionClosed();
+ void _q_networkSessionNewConfigurationActivated();
+ void _q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &config,
+ bool isSeamless);
+ void _q_networkSessionStateChanged(QNetworkSession::State state);
+#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;
+ QNetworkProxyFactory *proxyFactory;
+#endif
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession;
+ QNetworkSession::State lastSessionState;
+ QString networkConfiguration;
+ QNetworkAccessManager::NetworkAccessibility networkAccessible;
+ bool online;
+ bool initializeSession;
+#endif
+
+ 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;
+ static inline QNetworkAccessCache *getObjectCache(QNetworkAccessBackend *backend)
+ { return &backend->manager->objectCache; }
+ Q_AUTOTEST_EXPORT static void clearCache(QNetworkAccessManager *manager);
+
+ Q_DECLARE_PUBLIC(QNetworkAccessManager)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
new file mode 100644
index 0000000000..52eb3453b8
--- /dev/null
+++ b/src/network/access/qnetworkcookie.cpp
@@ -0,0 +1,1052 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkcookie.h"
+#include "qnetworkcookie_p.h"
+
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "QtCore/qbytearray.h"
+#include "QtCore/qdebug.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qlocale.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+#include "QtCore/qurl.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkCookie
+ \since 4.4
+ \brief The QNetworkCookie class holds one network cookie.
+
+ Cookies are small bits of information that stateless protocols
+ like HTTP use to maintain some persistent information across
+ requests.
+
+ A cookie is set by a remote server when it replies to a request
+ and it expects the same cookie to be sent back when further
+ requests are sent.
+
+ QNetworkCookie holds one such cookie as received from the
+ network. A cookie has a name and a value, but those are opaque to
+ the application (that is, the information stored in them has no
+ meaning to the application). A cookie has an associated path name
+ and domain, which indicate when the cookie should be sent again to
+ the server.
+
+ A cookie can also have an expiration date, indicating its
+ validity. If the expiration date is not present, the cookie is
+ considered a "session cookie" and should be discarded when the
+ application exits (or when its concept of session is over).
+
+ QNetworkCookie provides a way of parsing a cookie from the HTTP
+ header format using the QNetworkCookie::parseCookies()
+ function. However, when received in a QNetworkReply, the cookie is
+ already parsed.
+
+ This class implements cookies as described by the
+ \l{Netscape Cookie Specification}{initial cookie specification by
+ Netscape}, which is somewhat similar to the \l{RFC 2109} specification,
+ plus the \l{Mitigating Cross-site Scripting With HTTP-only Cookies}
+ {"HttpOnly" extension}. The more recent \l{RFC 2965} specification
+ (which uses the Set-Cookie2 header) is not supported.
+
+ \sa QNetworkCookieJar, QNetworkRequest, QNetworkReply
+*/
+
+/*!
+ Create a new QNetworkCookie object, initializing the cookie name
+ to \a name and its value to \a value.
+
+ A cookie is only valid if it has a name. However, the value is
+ opaque to the application and being empty may have significance to
+ the remote server.
+*/
+QNetworkCookie::QNetworkCookie(const QByteArray &name, const QByteArray &value)
+ : d(new QNetworkCookiePrivate)
+{
+ qRegisterMetaType<QNetworkCookie>();
+ qRegisterMetaType<QList<QNetworkCookie> >();
+
+ d->name = name;
+ d->value = value;
+}
+
+/*!
+ Creates a new QNetworkCookie object by copying the contents of \a
+ other.
+*/
+QNetworkCookie::QNetworkCookie(const QNetworkCookie &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys this QNetworkCookie object.
+*/
+QNetworkCookie::~QNetworkCookie()
+{
+ // QSharedDataPointer auto deletes
+ d = 0;
+}
+
+/*!
+ Copies the contents of the QNetworkCookie object \a other to this
+ object.
+*/
+QNetworkCookie &QNetworkCookie::operator=(const QNetworkCookie &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \fn bool QNetworkCookie::operator!=(const QNetworkCookie &other) const
+
+ Returns true if this cookie is not equal to \a other.
+
+ \sa operator==()
+*/
+
+/*!
+ Returns true if this cookie is equal to \a other. This function
+ only returns true if all fields of the cookie are the same.
+
+ However, in some contexts, two cookies of the same name could be
+ considered equal.
+
+ \sa operator!=()
+*/
+bool QNetworkCookie::operator==(const QNetworkCookie &other) const
+{
+ if (d == other.d)
+ return true;
+ return d->name == other.d->name &&
+ d->value == other.d->value &&
+ d->expirationDate.toUTC() == other.d->expirationDate.toUTC() &&
+ d->domain == other.d->domain &&
+ d->path == other.d->path &&
+ d->secure == other.d->secure &&
+ d->comment == other.d->comment;
+}
+
+/*!
+ Returns true if the "secure" option was specified in the cookie
+ string, false otherwise.
+
+ Secure cookies may contain private information and should not be
+ resent over unencrypted connections.
+
+ \sa setSecure()
+*/
+bool QNetworkCookie::isSecure() const
+{
+ return d->secure;
+}
+
+/*!
+ Sets the secure flag of this cookie to \a enable.
+
+ Secure cookies may contain private information and should not be
+ resent over unencrypted connections.
+
+ \sa isSecure()
+*/
+void QNetworkCookie::setSecure(bool enable)
+{
+ d->secure = enable;
+}
+
+/*!
+ \since 4.5
+
+ Returns true if the "HttpOnly" flag is enabled for this cookie.
+
+ A cookie that is "HttpOnly" is only set and retrieved by the
+ network requests and replies; i.e., the HTTP protocol. It is not
+ accessible from scripts running on browsers.
+
+ \sa isSecure()
+*/
+bool QNetworkCookie::isHttpOnly() const
+{
+ return d->httpOnly;
+}
+
+/*!
+ \since 4.5
+
+ Sets this cookie's "HttpOnly" flag to \a enable.
+*/
+void QNetworkCookie::setHttpOnly(bool enable)
+{
+ d->httpOnly = enable;
+}
+
+/*!
+ Returns true if this cookie is a session cookie. A session cookie
+ is a cookie which has no expiration date, which means it should be
+ discarded when the application's concept of session is over
+ (usually, when the application exits).
+
+ \sa expirationDate(), setExpirationDate()
+*/
+bool QNetworkCookie::isSessionCookie() const
+{
+ return !d->expirationDate.isValid();
+}
+
+/*!
+ Returns the expiration date for this cookie. If this cookie is a
+ session cookie, the QDateTime returned will not be valid. If the
+ date is in the past, this cookie has already expired and should
+ not be sent again back to a remote server.
+
+ The expiration date corresponds to the parameters of the "expires"
+ entry in the cookie string.
+
+ \sa isSessionCookie(), setExpirationDate()
+*/
+QDateTime QNetworkCookie::expirationDate() const
+{
+ return d->expirationDate;
+}
+
+/*!
+ Sets the expiration date of this cookie to \a date. Setting an
+ invalid expiration date to this cookie will mean it's a session
+ cookie.
+
+ \sa isSessionCookie(), expirationDate()
+*/
+void QNetworkCookie::setExpirationDate(const QDateTime &date)
+{
+ d->expirationDate = date;
+}
+
+/*!
+ Returns the domain this cookie is associated with. This
+ corresponds to the "domain" field of the cookie string.
+
+ Note that the domain here may start with a dot, which is not a
+ valid hostname. However, it means this cookie matches all
+ hostnames ending with that domain name.
+
+ \sa setDomain()
+*/
+QString QNetworkCookie::domain() const
+{
+ return d->domain;
+}
+
+/*!
+ Sets the domain associated with this cookie to be \a domain.
+
+ \sa domain()
+*/
+void QNetworkCookie::setDomain(const QString &domain)
+{
+ d->domain = domain;
+}
+
+/*!
+ Returns the path associated with this cookie. This corresponds to
+ the "path" field of the cookie string.
+
+ \sa setPath()
+*/
+QString QNetworkCookie::path() const
+{
+ return d->path;
+}
+
+/*!
+ Sets the path associated with this cookie to be \a path.
+
+ \sa path()
+*/
+void QNetworkCookie::setPath(const QString &path)
+{
+ d->path = path;
+}
+
+/*!
+ Returns the name of this cookie. The only mandatory field of a
+ cookie is its name, without which it is not considered valid.
+
+ \sa setName(), value()
+*/
+QByteArray QNetworkCookie::name() const
+{
+ return d->name;
+}
+
+/*!
+ Sets the name of this cookie to be \a cookieName. Note that
+ setting a cookie name to an empty QByteArray will make this cookie
+ invalid.
+
+ \sa name(), value()
+*/
+void QNetworkCookie::setName(const QByteArray &cookieName)
+{
+ d->name = cookieName;
+}
+
+/*!
+ Returns this cookies value, as specified in the cookie
+ string. Note that a cookie is still valid if its value is empty.
+
+ Cookie name-value pairs are considered opaque to the application:
+ that is, their values don't mean anything.
+
+ \sa setValue(), name()
+*/
+QByteArray QNetworkCookie::value() const
+{
+ return d->value;
+}
+
+/*!
+ Sets the value of this cookie to be \a value.
+
+ \sa value(), name()
+*/
+void QNetworkCookie::setValue(const QByteArray &value)
+{
+ d->value = value;
+}
+
+// ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
+static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
+{
+ // format is one of:
+ // (1) token
+ // (2) token = token
+ // (3) token = quoted-string
+ int i;
+ const int length = text.length();
+ position = nextNonWhitespace(text, position);
+
+ // parse the first part, before the equal sign
+ for (i = position; i < length; ++i) {
+ register char c = text.at(i);
+ if (c == ';' || c == ',' || c == '=')
+ break;
+ }
+
+ QByteArray first = text.mid(position, i - position).trimmed();
+ position = i;
+
+ if (first.isEmpty())
+ return qMakePair(QByteArray(), QByteArray());
+ if (i == length || text.at(i) != '=')
+ // no equal sign, we found format (1)
+ return qMakePair(first, QByteArray());
+
+ QByteArray second;
+ second.reserve(32); // arbitrary but works for most cases
+
+ i = nextNonWhitespace(text, position + 1);
+ if (i < length && text.at(i) == '"') {
+ // a quote, we found format (3), where:
+ // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ // 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 (isNameValue)
+ second += '"';
+ ++i;
+ while (i < length) {
+ register char c = text.at(i);
+ if (c == '"') {
+ // end of quoted text
+ if (isNameValue)
+ second += '"';
+ break;
+ } else if (c == '\\') {
+ if (isNameValue)
+ second += '\\';
+ ++i;
+ if (i >= length)
+ // broken line
+ return qMakePair(QByteArray(), QByteArray());
+ c = text.at(i);
+ }
+
+ second += c;
+ ++i;
+ }
+
+ for ( ; i < length; ++i) {
+ register char c = text.at(i);
+ if (c == ',' || c == ';')
+ break;
+ }
+ position = i;
+ } else {
+ // no quote, we found format (2)
+ position = i;
+ for ( ; i < length; ++i) {
+ register char c = text.at(i);
+ if (c == ',' || c == ';' || isLWS(c))
+ break;
+ }
+
+ second = text.mid(position, i - position).trimmed();
+ position = i;
+ }
+
+ if (second.isNull())
+ second.resize(0); // turns into empty-but-not-null
+ return qMakePair(first, second);
+}
+
+/*!
+ \enum QNetworkCookie::RawForm
+
+ This enum is used with the toRawForm() function to declare which
+ form of a cookie shall be returned.
+
+ \value NameAndValueOnly makes toRawForm() return only the
+ "NAME=VALUE" part of the cookie, as suitable for sending back
+ to a server in a client request's "Cookie:" header. Multiple
+ cookies are separated by a semi-colon in the "Cookie:" header
+ field.
+
+ \value Full makes toRawForm() return the full
+ cookie contents, as suitable for sending to a client in a
+ server's "Set-Cookie:" header. Multiple cookies are separated
+ by commas in a "Set-Cookie:" header.
+
+ Note that only the Full form of the cookie can be parsed back into
+ its original contents.
+
+ \sa toRawForm(), parseCookies()
+*/
+
+/*!
+ Returns the raw form of this QNetworkCookie. The QByteArray
+ returned by this function is suitable for an HTTP header, either
+ in a server response (the Set-Cookie header) or the client request
+ (the Cookie header). You can choose from one of two formats, using
+ \a form.
+
+ \sa parseCookies()
+*/
+QByteArray QNetworkCookie::toRawForm(RawForm form) const
+{
+ QByteArray result;
+ if (d->name.isEmpty())
+ return result; // not a valid cookie
+
+ result = d->name;
+ result += '=';
+ if ((d->value.contains(';') ||
+ d->value.contains(',') ||
+ d->value.contains(' ') ||
+ d->value.contains('"')) &&
+ (!d->value.startsWith('"') &&
+ !d->value.endsWith('"'))) {
+ result += '"';
+
+ QByteArray value = d->value;
+ value.replace('"', "\\\"");
+ result += value;
+
+ result += '"';
+ } else {
+ result += d->value;
+ }
+
+ if (form == Full) {
+ // same as above, but encoding everything back
+ if (isSecure())
+ result += "; secure";
+ if (isHttpOnly())
+ result += "; HttpOnly";
+ if (!isSessionCookie()) {
+ result += "; expires=";
+ result += QLocale::c().toString(d->expirationDate.toUTC(),
+ QLatin1String("ddd, dd-MMM-yyyy hh:mm:ss 'GMT")).toLatin1();
+ }
+ if (!d->domain.isEmpty()) {
+ result += "; domain=";
+ QString domainNoDot = d->domain;
+ if (domainNoDot.startsWith(QLatin1Char('.'))) {
+ result += '.';
+ domainNoDot = domainNoDot.mid(1);
+ }
+ result += QUrl::toAce(domainNoDot);
+ }
+ if (!d->path.isEmpty()) {
+ result += "; path=";
+ result += QUrl::toPercentEncoding(d->path, "/");
+ }
+ }
+ return result;
+}
+
+static const char zones[] =
+ "pst\0" // -8
+ "pdt\0"
+ "mst\0" // -7
+ "mdt\0"
+ "cst\0" // -6
+ "cdt\0"
+ "est\0" // -5
+ "edt\0"
+ "ast\0" // -4
+ "nst\0" // -3
+ "gmt\0" // 0
+ "utc\0"
+ "bst\0"
+ "met\0" // 1
+ "eet\0" // 2
+ "jst\0" // 9
+ "\0";
+static int zoneOffsets[] = {-8, -8, -7, -7, -6, -6, -5, -5, -4, -3, 0, 0, 0, 1, 2, 9 };
+
+static const char months[] =
+ "jan\0"
+ "feb\0"
+ "mar\0"
+ "apr\0"
+ "may\0"
+ "jun\0"
+ "jul\0"
+ "aug\0"
+ "sep\0"
+ "oct\0"
+ "nov\0"
+ "dec\0"
+ "\0";
+
+static inline bool isNumber(char s)
+{ return s >= '0' && s <= '9'; }
+
+static inline bool isTerminator(char c)
+{ return c == '\n' || c == '\r'; }
+
+static inline bool isValueSeparator(char c)
+{ return isTerminator(c) || c == ';'; }
+
+static inline bool isWhitespace(char c)
+{ return c == ' ' || c == '\t'; }
+
+static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
+{
+ if (dateString[at] < 'a' || dateString[at] > 'z')
+ return false;
+ if (val == -1 && dateString.length() >= at + 3) {
+ int j = 0;
+ int i = 0;
+ while (i <= size) {
+ const char *str = array + i;
+ if (str[0] == dateString[at]
+ && str[1] == dateString[at + 1]
+ && str[2] == dateString[at + 2]) {
+ val = j;
+ return true;
+ }
+ i += strlen(str) + 1;
+ ++j;
+ }
+ }
+ return false;
+}
+
+//#define PARSEDATESTRINGDEBUG
+
+#define ADAY 1
+#define AMONTH 2
+#define AYEAR 4
+
+/*
+ Parse all the date formats that Firefox can.
+
+ The official format is:
+ expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
+
+ But browsers have been supporting a very wide range of date
+ strings. To work on many sites we need to support more then
+ just the official date format.
+
+ For reference see Firefox's PR_ParseTimeStringToExplodedTime in
+ prtime.c. The Firefox date parser is coded in a very complex way
+ and is slightly over ~700 lines long. While this implementation
+ will be slightly slower for the non standard dates it is smaller,
+ more readable, and maintainable.
+
+ Or in their own words:
+ "} // else what the hell is this."
+*/
+static QDateTime parseDateString(const QByteArray &dateString)
+{
+ QTime time;
+ // placeholders for values when we are not sure it is a year, month or day
+ int unknown[3] = {-1, -1, -1};
+ int month = -1;
+ int day = -1;
+ int year = -1;
+ int zoneOffset = -1;
+
+ // hour:minute:second.ms pm
+ QRegExp timeRx(QLatin1String("(\\d{1,2}):(\\d{1,2})(:(\\d{1,2})|)(\\.(\\d{1,3})|)((\\s{0,}(am|pm))|)"));
+
+ int at = 0;
+ while (at < dateString.length()) {
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << dateString.mid(at);
+#endif
+ bool isNum = isNumber(dateString[at]);
+
+ // Month
+ if (!isNum
+ && checkStaticArray(month, dateString, at, months, sizeof(months)- 1)) {
+ ++month;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Month:" << month;
+#endif
+ at += 3;
+ continue;
+ }
+ // Zone
+ if (!isNum
+ && zoneOffset == -1
+ && checkStaticArray(zoneOffset, dateString, at, zones, sizeof(zones)- 1)) {
+ int sign = (at >= 0 && dateString[at - 1] == '-') ? -1 : 1;
+ zoneOffset = sign * zoneOffsets[zoneOffset] * 60 * 60;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Zone:" << month;
+#endif
+ at += 3;
+ continue;
+ }
+ // Zone offset
+ if (!isNum
+ && (zoneOffset == -1 || zoneOffset == 0) // Can only go after gmt
+ && (dateString[at] == '+' || dateString[at] == '-')
+ && (at == 0
+ || isWhitespace(dateString[at - 1])
+ || dateString[at - 1] == ','
+ || (at >= 3
+ && (dateString[at - 3] == 'g')
+ && (dateString[at - 2] == 'm')
+ && (dateString[at - 1] == 't')))) {
+
+ int end = 1;
+ while (end < 5 && dateString.length() > at+end
+ && dateString[at + end] >= '0' && dateString[at + end] <= '9')
+ ++end;
+ int minutes = 0;
+ int hours = 0;
+ switch (end - 1) {
+ case 4:
+ minutes = atoi(dateString.mid(at + 3, 2).constData());
+ // fall through
+ case 2:
+ hours = atoi(dateString.mid(at + 1, 2).constData());
+ break;
+ case 1:
+ hours = atoi(dateString.mid(at + 1, 1).constData());
+ break;
+ default:
+ at += end;
+ continue;
+ }
+ if (end != 1) {
+ int sign = dateString[at] == '-' ? -1 : 1;
+ zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Zone offset:" << zoneOffset << hours << minutes;
+#endif
+ at += end;
+ continue;
+ }
+ }
+
+ // Time
+ if (isNum && time.isNull()
+ && dateString.length() >= at + 3
+ && (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
+ // While the date can be found all over the string the format
+ // for the time is set and a nice regexp can be used.
+ int pos = timeRx.indexIn(QLatin1String(dateString), at);
+ if (pos != -1) {
+ QStringList list = timeRx.capturedTexts();
+ int h = atoi(list.at(1).toLatin1().constData());
+ int m = atoi(list.at(2).toLatin1().constData());
+ int s = atoi(list.at(4).toLatin1().constData());
+ int ms = atoi(list.at(6).toLatin1().constData());
+ if (h < 12 && !list.at(9).isEmpty())
+ if (list.at(9) == QLatin1String("pm"))
+ h += 12;
+ time = QTime(h, m, s, ms);
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Time:" << list << timeRx.matchedLength();
+#endif
+ at += timeRx.matchedLength();
+ continue;
+ }
+ }
+
+ // 4 digit Year
+ if (isNum
+ && year == -1
+ && dateString.length() > at + 3) {
+ if (isNumber(dateString[at + 1])
+ && isNumber(dateString[at + 2])
+ && isNumber(dateString[at + 3])) {
+ year = atoi(dateString.mid(at, 4).constData());
+ at += 4;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Year:" << year;
+#endif
+ continue;
+ }
+ }
+
+ // a one or two digit number
+ // Could be month, day or year
+ if (isNum) {
+ int length = 1;
+ if (dateString.length() > at + 1
+ && isNumber(dateString[at + 1]))
+ ++length;
+ int x = atoi(dateString.mid(at, length).constData());
+ if (year == -1 && (x > 31 || x == 0)) {
+ year = x;
+ } else {
+ if (unknown[0] == -1) unknown[0] = x;
+ else if (unknown[1] == -1) unknown[1] = x;
+ else if (unknown[2] == -1) unknown[2] = x;
+ }
+ at += length;
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Saving" << x;
+#endif
+ continue;
+ }
+
+ // Unknown character, typically a weekday such as 'Mon'
+ ++at;
+ }
+
+ // Once we are done parsing the string take the digits in unknown
+ // and determine which is the unknown year/month/day
+
+ int couldBe[3] = { 0, 0, 0 };
+ int unknownCount = 3;
+ for (int i = 0; i < unknownCount; ++i) {
+ if (unknown[i] == -1) {
+ couldBe[i] = ADAY | AYEAR | AMONTH;
+ unknownCount = i;
+ continue;
+ }
+
+ if (unknown[i] >= 1)
+ couldBe[i] = ADAY;
+
+ if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
+ couldBe[i] |= AMONTH;
+
+ if (year == -1)
+ couldBe[i] |= AYEAR;
+ }
+
+ // For any possible day make sure one of the values that could be a month
+ // can contain that day.
+ // For any possible month make sure one of the values that can be a
+ // day that month can have.
+ // Example: 31 11 06
+ // 31 can't be a day because 11 and 6 don't have 31 days
+ for (int i = 0; i < unknownCount; ++i) {
+ int currentValue = unknown[i];
+ bool findMatchingMonth = couldBe[i] & ADAY && currentValue >= 29;
+ bool findMatchingDay = couldBe[i] & AMONTH;
+ if (!findMatchingMonth || !findMatchingDay)
+ continue;
+ for (int j = 0; j < 3; ++j) {
+ if (j == i)
+ continue;
+ for (int k = 0; k < 2; ++k) {
+ if (k == 0 && !(findMatchingMonth && (couldBe[j] & AMONTH)))
+ continue;
+ else if (k == 1 && !(findMatchingDay && (couldBe[j] & ADAY)))
+ continue;
+ int m = currentValue;
+ int d = unknown[j];
+ if (k == 0)
+ qSwap(m, d);
+ if (m == -1) m = month;
+ bool found = true;
+ switch(m) {
+ case 2:
+ // When we get 29 and the year ends up having only 28
+ // See date.isValid below
+ // Example: 29 23 Feb
+ if (d <= 29)
+ found = false;
+ break;
+ case 4: case 6: case 9: case 11:
+ if (d <= 30)
+ found = false;
+ break;
+ default:
+ if (d > 0 && d <= 31)
+ found = false;
+ }
+ if (k == 0) findMatchingMonth = found;
+ else if (k == 1) findMatchingDay = found;
+ }
+ }
+ if (findMatchingMonth)
+ couldBe[i] &= ~ADAY;
+ if (findMatchingDay)
+ couldBe[i] &= ~AMONTH;
+ }
+
+ // First set the year/month/day that have been deduced
+ // and reduce the set as we go along to deduce more
+ for (int i = 0; i < unknownCount; ++i) {
+ int unset = 0;
+ for (int j = 0; j < 3; ++j) {
+ if (couldBe[j] == ADAY && day == -1) {
+ day = unknown[j];
+ unset |= ADAY;
+ } else if (couldBe[j] == AMONTH && month == -1) {
+ month = unknown[j];
+ unset |= AMONTH;
+ } else if (couldBe[j] == AYEAR && year == -1) {
+ year = unknown[j];
+ unset |= AYEAR;
+ } else {
+ // common case
+ break;
+ }
+ couldBe[j] &= ~unset;
+ }
+ }
+
+ // Now fallback to a standardized order to fill in the rest with
+ for (int i = 0; i < unknownCount; ++i) {
+ if (couldBe[i] & AMONTH && month == -1) month = unknown[i];
+ else if (couldBe[i] & ADAY && day == -1) day = unknown[i];
+ else if (couldBe[i] & AYEAR && year == -1) year = unknown[i];
+ }
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Final set" << year << month << day;
+#endif
+
+ if (year == -1 || month == -1 || day == -1) {
+#ifdef PARSEDATESTRINGDEBUG
+ qDebug() << "Parser failure" << year << month << day;
+#endif
+ return QDateTime();
+ }
+
+ // Y2k behavior
+ int y2k = 0;
+ if (year < 70)
+ y2k = 2000;
+ else if (year < 100)
+ y2k = 1900;
+
+ QDate date(year + y2k, month, day);
+
+ // When we were given a bad cookie that when parsed
+ // set the day to 29 and the year to one that doesn't
+ // have the 29th of Feb rather then adding the extra
+ // complicated checking earlier just swap here.
+ // Example: 29 23 Feb
+ if (!date.isValid())
+ date = QDate(day + y2k, month, year);
+
+ QDateTime dateTime(date, time, Qt::UTC);
+
+ if (zoneOffset != -1) {
+ dateTime = dateTime.addSecs(zoneOffset);
+ }
+ if (!dateTime.isValid())
+ return QDateTime();
+ return dateTime;
+}
+
+/*!
+ Parses the cookie string \a cookieString as received from a server
+ response in the "Set-Cookie:" header. If there's a parsing error,
+ this function returns an empty list.
+
+ Since the HTTP header can set more than one cookie at the same
+ time, this function returns a QList<QNetworkCookie>, one for each
+ cookie that is parsed.
+
+ \sa toRawForm()
+*/
+QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
+{
+ // cookieString can be a number of set-cookie header strings joined together
+ // by \n, parse each line separately.
+ QList<QNetworkCookie> cookies;
+ QList<QByteArray> list = cookieString.split('\n');
+ for (int a = 0; a < list.size(); a++)
+ cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
+ return cookies;
+}
+
+QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
+{
+ // According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
+ // the Set-Cookie response header is of the format:
+ //
+ // Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
+ //
+ // where only the NAME=VALUE part is mandatory
+ //
+ // We do not support RFC 2965 Set-Cookie2-style cookies
+
+ QList<QNetworkCookie> result;
+ QDateTime now = QDateTime::currentDateTime().toUTC();
+
+ int position = 0;
+ const int length = cookieString.length();
+ while (position < length) {
+ QNetworkCookie cookie;
+
+ // The first part is always the "NAME=VALUE" part
+ QPair<QByteArray,QByteArray> field = nextField(cookieString, position, true);
+ if (field.first.isEmpty() || field.second.isNull())
+ // parsing error
+ break;
+ cookie.setName(field.first);
+ cookie.setValue(field.second);
+
+ position = nextNonWhitespace(cookieString, position);
+ bool endOfCookie = false;
+ while (!endOfCookie && position < length) {
+ switch (cookieString.at(position++)) {
+ case ',':
+ // end of the cookie
+ endOfCookie = true;
+ break;
+
+ case ';':
+ // new field in the cookie
+ field = nextField(cookieString, position, false);
+ field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
+
+ if (field.first == "expires") {
+ position -= field.second.length();
+ int end;
+ for (end = position; end < length; ++end)
+ if (isValueSeparator(cookieString.at(end)))
+ break;
+
+ QByteArray dateString = cookieString.mid(position, end - position).trimmed();
+ position = end;
+ QDateTime dt = parseDateString(dateString.toLower());
+ if (!dt.isValid()) {
+ return result;
+ }
+ cookie.setExpirationDate(dt);
+ } else if (field.first == "domain") {
+ QByteArray rawDomain = field.second;
+ QString maybeLeadingDot;
+ if (rawDomain.startsWith('.')) {
+ maybeLeadingDot = QLatin1Char('.');
+ rawDomain = rawDomain.mid(1);
+ }
+
+ QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain)));
+ if (normalizedDomain.isEmpty() && !rawDomain.isEmpty())
+ return result;
+ cookie.setDomain(maybeLeadingDot + normalizedDomain);
+ } else if (field.first == "max-age") {
+ bool ok = false;
+ int secs = field.second.toInt(&ok);
+ if (!ok)
+ return result;
+ cookie.setExpirationDate(now.addSecs(secs));
+ } else if (field.first == "path") {
+ QString path = QUrl::fromPercentEncoding(field.second);
+ cookie.setPath(path);
+ } else if (field.first == "secure") {
+ cookie.setSecure(true);
+ } else if (field.first == "httponly") {
+ cookie.setHttpOnly(true);
+ } else if (field.first == "comment") {
+ //cookie.setComment(QString::fromUtf8(field.second));
+ } else if (field.first == "version") {
+ if (field.second != "1") {
+ // oops, we don't know how to handle this cookie
+ return result;
+ }
+ } else {
+ // got an unknown field in the cookie
+ // what do we do?
+ }
+
+ position = nextNonWhitespace(cookieString, position);
+ }
+ }
+
+ if (!cookie.name().isEmpty())
+ result += cookie;
+ }
+
+ return result;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
+{
+ s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ')';
+ return s.space();
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
new file mode 100644
index 0000000000..6060b1a896
--- /dev/null
+++ b/src/network/access/qnetworkcookie.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCOOKIE_H
+#define QNETWORKCOOKIE_H
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QList>
+#include <QtCore/QMetaType>
+#include <QtCore/QObject>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QByteArray;
+class QDateTime;
+class QString;
+class QUrl;
+
+class QNetworkCookiePrivate;
+class Q_NETWORK_EXPORT QNetworkCookie
+{
+public:
+ enum RawForm {
+ NameAndValueOnly,
+ Full
+ };
+
+ QNetworkCookie(const QByteArray &name = QByteArray(), const QByteArray &value = QByteArray());
+ QNetworkCookie(const QNetworkCookie &other);
+ ~QNetworkCookie();
+ QNetworkCookie &operator=(const QNetworkCookie &other);
+ bool operator==(const QNetworkCookie &other) const;
+ inline bool operator!=(const QNetworkCookie &other) const
+ { return !(*this == other); }
+
+ bool isSecure() const;
+ void setSecure(bool enable);
+ bool isHttpOnly() const;
+ void setHttpOnly(bool enable);
+
+ bool isSessionCookie() const;
+ QDateTime expirationDate() const;
+ void setExpirationDate(const QDateTime &date);
+
+ QString domain() const;
+ void setDomain(const QString &domain);
+
+ QString path() const;
+ void setPath(const QString &path);
+
+ QByteArray name() const;
+ void setName(const QByteArray &cookieName);
+
+ QByteArray value() const;
+ void setValue(const QByteArray &value);
+
+ QByteArray toRawForm(RawForm form = Full) const;
+
+ static QList<QNetworkCookie> parseCookies(const QByteArray &cookieString);
+
+private:
+ QSharedDataPointer<QNetworkCookiePrivate> d;
+ friend class QNetworkCookiePrivate;
+};
+Q_DECLARE_TYPEINFO(QNetworkCookie, Q_MOVABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QNetworkCookie &);
+#endif
+
+QT_END_NAMESPACE
+
+// ### Qt5 remove this include
+#include <QtNetwork/QNetworkCookieJar>
+
+Q_DECLARE_METATYPE(QNetworkCookie)
+Q_DECLARE_METATYPE(QList<QNetworkCookie>)
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h
new file mode 100644
index 0000000000..0d6dd70c03
--- /dev/null
+++ b/src/network/access/qnetworkcookie_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCOOKIE_P_H
+#define QNETWORKCOOKIE_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.
+//
+
+#include "QtCore/qdatetime.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkCookiePrivate: public QSharedData
+{
+public:
+ inline QNetworkCookiePrivate() : secure(false), httpOnly(false) { }
+ static QList<QNetworkCookie> parseSetCookieHeaderLine(const QByteArray &cookieString);
+
+ QDateTime expirationDate;
+ QString domain;
+ QString path;
+ QString comment;
+ QByteArray name;
+ QByteArray value;
+ bool secure;
+ bool httpOnly;
+};
+
+static inline bool isLWS(register char c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static int nextNonWhitespace(const QByteArray &text, int from)
+{
+ // RFC 2616 defines linear whitespace as:
+ // LWS = [CRLF] 1*( SP | HT )
+ // We ignore the fact that CRLF must come as a pair at this point
+ // It's an invalid HTTP header if that happens.
+ while (from < text.length()) {
+ if (isLWS(text.at(from)))
+ ++from;
+ else
+ return from; // non-whitespace
+ }
+
+ // reached the end
+ return text.length();
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
new file mode 100644
index 0000000000..53fab9f0c4
--- /dev/null
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkcookiejar.h"
+#include "qnetworkcookiejartlds_p.h"
+#include "qnetworkcookiejar_p.h"
+
+#include "QtNetwork/qnetworkcookie.h"
+#include "QtCore/qurl.h"
+#include "QtCore/qdatetime.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkCookieJar
+ \brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
+ \since 4.4
+
+ Cookies are small bits of information that stateless protocols
+ like HTTP use to maintain some persistent information across
+ requests.
+
+ A cookie is set by a remote server when it replies to a request
+ and it expects the same cookie to be sent back when further
+ requests are sent.
+
+ The cookie jar is the object that holds all cookies set in
+ previous requests. Web browsers save their cookie jars to disk in
+ order to conserve permanent cookies across invocations of the
+ application.
+
+ QNetworkCookieJar does not implement permanent storage: it only
+ keeps the cookies in memory. Once the QNetworkCookieJar object is
+ deleted, all cookies it held will be discarded as well. If you
+ want to save the cookies, you should derive from this class and
+ implement the saving to disk to your own storage format.
+
+ This class implements only the basic security recommended by the
+ cookie specifications and does not implement any cookie acceptance
+ policy (it accepts all cookies set by any requests). In order to
+ override those rules, you should reimplement the
+ cookiesForUrl() and setCookiesFromUrl() virtual
+ functions. They are called by QNetworkReply and
+ QNetworkAccessManager when they detect new cookies and when they
+ require cookies.
+
+ \sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
+ QNetworkRequest, QNetworkAccessManager::setCookieJar()
+*/
+
+/*!
+ Creates a QNetworkCookieJar object and sets the parent object to
+ be \a parent.
+
+ The cookie jar is initialized to empty.
+*/
+QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
+ : QObject(*new QNetworkCookieJarPrivate, parent)
+{
+}
+
+/*!
+ Destroys this cookie jar object and discards all cookies stored in
+ it. Cookies are not saved to disk in the QNetworkCookieJar default
+ implementation.
+
+ If you need to save the cookies to disk, you have to derive from
+ QNetworkCookieJar and save the cookies to disk yourself.
+*/
+QNetworkCookieJar::~QNetworkCookieJar()
+{
+}
+
+/*!
+ Returns all cookies stored in this cookie jar. This function is
+ suitable for derived classes to save cookies to disk, as well as
+ to implement cookie expiration and other policies.
+
+ \sa setAllCookies(), cookiesForUrl()
+*/
+QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
+{
+ return d_func()->allCookies;
+}
+
+/*!
+ Sets the internal list of cookies held by this cookie jar to be \a
+ cookieList. This function is suitable for derived classes to
+ implement loading cookies from permanent storage, or their own
+ cookie acceptance policies by reimplementing
+ setCookiesFromUrl().
+
+ \sa allCookies(), setCookiesFromUrl()
+*/
+void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
+{
+ Q_D(QNetworkCookieJar);
+ d->allCookies = cookieList;
+}
+
+static inline bool isParentPath(QString path, QString reference)
+{
+ if (!path.endsWith(QLatin1Char('/')))
+ path += QLatin1Char('/');
+ if (!reference.endsWith(QLatin1Char('/')))
+ reference += QLatin1Char('/');
+ return path.startsWith(reference);
+}
+
+static inline bool isParentDomain(QString domain, QString reference)
+{
+ if (!reference.startsWith(QLatin1Char('.')))
+ return domain == reference;
+
+ return domain.endsWith(reference) || domain == reference.mid(1);
+}
+
+/*!
+ Adds the cookies in the list \a cookieList to this cookie
+ jar. Default values for path and domain are taken from the \a
+ url object.
+
+ Returns true if one or more cookies are set for \a url,
+ otherwise false.
+
+ If a cookie already exists in the cookie jar, it will be
+ overridden by those in \a cookieList.
+
+ The default QNetworkCookieJar class implements only a very basic
+ security policy (it makes sure that the cookies' domain and path
+ match the reply's). To enhance the security policy with your own
+ algorithms, override setCookiesFromUrl().
+
+ Also, QNetworkCookieJar does not have a maximum cookie jar
+ size. Reimplement this function to discard older cookies to create
+ room for new ones.
+
+ \sa cookiesForUrl(), QNetworkAccessManager::setCookieJar()
+*/
+bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
+ const QUrl &url)
+{
+ Q_D(QNetworkCookieJar);
+ QString defaultDomain = url.host();
+ QString pathAndFileName = url.path();
+ QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
+ if (defaultPath.isEmpty())
+ defaultPath = QLatin1Char('/');
+
+ int added = 0;
+ QDateTime now = QDateTime::currentDateTime();
+ foreach (QNetworkCookie cookie, cookieList) {
+ bool isDeletion = !cookie.isSessionCookie() &&
+ cookie.expirationDate() < now;
+
+ // validate the cookie & set the defaults if unset
+ if (cookie.path().isEmpty())
+ cookie.setPath(defaultPath);
+ // don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
+// else if (!isParentPath(pathAndFileName, cookie.path())) {
+// continue; // not accepted
+// }
+ if (cookie.domain().isEmpty()) {
+ cookie.setDomain(defaultDomain);
+ } else {
+ // Ensure the domain starts with a dot if its field was not empty
+ // in the HTTP header. There are some servers that forget the
+ // leading dot and this is actually forbidden according to RFC 2109,
+ // but all browsers accept it anyway so we do that as well.
+ if (!cookie.domain().startsWith(QLatin1Char('.')))
+ cookie.setDomain(QLatin1Char('.') + cookie.domain());
+
+ QString domain = cookie.domain();
+ if (!(isParentDomain(domain, defaultDomain)
+ || isParentDomain(defaultDomain, domain)))
+ continue; // not accepted
+
+ // 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)))
+ continue; // not accepted
+ }
+
+ QList<QNetworkCookie>::Iterator it = d->allCookies.begin(),
+ end = d->allCookies.end();
+ for ( ; it != end; ++it)
+ // does this cookie already exist?
+ if (cookie.name() == it->name() &&
+ cookie.domain() == it->domain() &&
+ cookie.path() == it->path()) {
+ // found a match
+ d->allCookies.erase(it);
+ break;
+ }
+
+ // did not find a match
+ if (!isDeletion) {
+ d->allCookies += cookie;
+ ++added;
+ }
+ }
+ return (added > 0);
+}
+
+/*!
+ Returns the cookies to be added to when a request is sent to
+ \a url. This function is called by the default
+ QNetworkAccessManager::createRequest(), which adds the
+ cookies returned by this function to the request being sent.
+
+ If more than one cookie with the same name is found, but with
+ differing paths, the one with longer path is returned before the
+ one with shorter path. In other words, this function returns
+ cookies sorted decreasingly by path length.
+
+ The default QNetworkCookieJar class implements only a very basic
+ security policy (it makes sure that the cookies' domain and path
+ match the reply's). To enhance the security policy with your own
+ algorithms, override cookiesForUrl().
+
+ \sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
+*/
+QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
+{
+// \b Warning! This is only a dumb implementation!
+// It does NOT follow all of the recommendations from
+// http://wp.netscape.com/newsref/std/cookie_spec.html
+// It does not implement a very good cross-domain verification yet.
+
+ Q_D(const QNetworkCookieJar);
+ QDateTime now = QDateTime::currentDateTime();
+ QList<QNetworkCookie> result;
+ bool isEncrypted = url.scheme().toLower() == QLatin1String("https");
+
+ // scan our cookies for something that matches
+ QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
+ end = d->allCookies.constEnd();
+ for ( ; it != end; ++it) {
+ if (!isParentDomain(url.host(), it->domain()))
+ continue;
+ if (!isParentPath(url.path(), it->path()))
+ continue;
+ if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
+ continue;
+ if ((*it).isSecure() && !isEncrypted)
+ continue;
+
+ // insert this cookie into result, sorted by path
+ QList<QNetworkCookie>::Iterator insertIt = result.begin();
+ while (insertIt != result.end()) {
+ if (insertIt->path().length() < it->path().length()) {
+ // insert here
+ insertIt = result.insert(insertIt, *it);
+ break;
+ } else {
+ ++insertIt;
+ }
+ }
+
+ // this is the shortest path yet, just append
+ if (insertIt == result.end())
+ result += *it;
+ }
+
+ 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.h b/src/network/access/qnetworkcookiejar.h
new file mode 100644
index 0000000000..46c0b9c9f7
--- /dev/null
+++ b/src/network/access/qnetworkcookiejar.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCOOKIEJAR_H
+#define QNETWORKCOOKIEJAR_H
+
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+
+// ### Qt5 remove this include
+#include <QtNetwork/QNetworkCookie>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QNetworkCookieJarPrivate;
+class Q_NETWORK_EXPORT QNetworkCookieJar: public QObject
+{
+ Q_OBJECT
+public:
+ QNetworkCookieJar(QObject *parent = 0);
+ virtual ~QNetworkCookieJar();
+
+ virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
+ virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
+
+protected:
+ QList<QNetworkCookie> allCookies() const;
+ void setAllCookies(const QList<QNetworkCookie> &cookieList);
+
+private:
+ Q_DECLARE_PRIVATE(QNetworkCookieJar)
+ Q_DISABLE_COPY(QNetworkCookieJar)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
new file mode 100644
index 0000000000..d6dc45057c
--- /dev/null
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCOOKIEJAR_P_H
+#define QNETWORKCOOKIEJAR_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.
+//
+
+#include "private/qobject_p.h"
+#include "qnetworkcookie.h"
+
+QT_BEGIN_NAMESPACE
+
+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)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkcookiejartlds_p.h b/src/network/access/qnetworkcookiejartlds_p.h
new file mode 100644
index 0000000000..b06d881131
--- /dev/null
+++ b/src/network/access/qnetworkcookiejartlds_p.h
@@ -0,0 +1,6481 @@
+// 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
new file mode 100644
index 0000000000..57a8d0e0cc
--- /dev/null
+++ b/src/network/access/qnetworkcookiejartlds_p.h.INFO
@@ -0,0 +1,17 @@
+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
new file mode 100644
index 0000000000..271494d008
--- /dev/null
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -0,0 +1,728 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNETWORKDISKCACHE_DEBUG
+
+
+#include "qnetworkdiskcache.h"
+#include "qnetworkdiskcache_p.h"
+#include "QtCore/qscopedpointer.h"
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qdiriterator.h>
+#include <qurl.h>
+#include <qcryptographichash.h>
+#include <qdebug.h>
+
+#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
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkDiskCache
+ \since 4.5
+ \inmodule QtNetwork
+
+ \brief The QNetworkDiskCache class provides a very basic disk cache.
+
+ QNetworkDiskCache stores each url in its own file inside of the
+ cacheDirectory using QDataStream. Files with a text MimeType
+ are compressed using qCompress. Each cache file starts with "cache_"
+ and ends in ".cache". Data is written to disk only in insert()
+ and updateMetaData().
+
+ Currently you can not share the same cache files with more then
+ one disk cache.
+
+ QNetworkDiskCache by default limits the amount of space that the cache will
+ use on the system to 50MB.
+
+ Note you have to set the cache directory before it will work.
+
+ A network disk cache can be enabled by:
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 0
+
+ When sending requests, to control the preference of when to use the cache
+ and when to use the network, consider the following:
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 1
+
+ To check whether the response came from the cache or from the network, the
+ following can be applied:
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 2
+*/
+
+/*!
+ Creates a new disk cache. The \a parent argument is passed to
+ QAbstractNetworkCache's constructor.
+ */
+QNetworkDiskCache::QNetworkDiskCache(QObject *parent)
+ : QAbstractNetworkCache(*new QNetworkDiskCachePrivate, parent)
+{
+}
+
+/*!
+ Destroys the cache object. This does not clear the disk cache.
+ */
+QNetworkDiskCache::~QNetworkDiskCache()
+{
+ Q_D(QNetworkDiskCache);
+ QHashIterator<QIODevice*, QCacheItem*> it(d->inserting);
+ while (it.hasNext()) {
+ it.next();
+ delete it.value();
+ }
+}
+
+/*!
+ Returns the location where cached files will be stored.
+*/
+QString QNetworkDiskCache::cacheDirectory() const
+{
+ Q_D(const QNetworkDiskCache);
+ return d->cacheDirectory;
+}
+
+/*!
+ Sets the directory where cached files will be stored to \a cacheDir
+
+ QNetworkDiskCache will create this directory if it does not exists.
+
+ Prepared cache items will be stored in the new cache directory when
+ they are inserted.
+
+ \sa QDesktopServices::CacheLocation
+*/
+void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::setCacheDirectory()" << cacheDir;
+#endif
+ Q_D(QNetworkDiskCache);
+ if (cacheDir.isEmpty())
+ return;
+ d->cacheDirectory = cacheDir;
+ QDir dir(d->cacheDirectory);
+ 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();
+}
+
+/*!
+ \reimp
+*/
+qint64 QNetworkDiskCache::cacheSize() const
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::cacheSize()";
+#endif
+ Q_D(const QNetworkDiskCache);
+ if (d->cacheDirectory.isEmpty())
+ return 0;
+ if (d->currentCacheSize < 0) {
+ QNetworkDiskCache *that = const_cast<QNetworkDiskCache*>(this);
+ that->d_func()->currentCacheSize = that->expire();
+ }
+ return d->currentCacheSize;
+}
+
+/*!
+ \reimp
+*/
+QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::prepare()" << metaData.url();
+#endif
+ Q_D(QNetworkDiskCache);
+ if (!metaData.isValid() || !metaData.url().isValid() || !metaData.saveToDisk())
+ return 0;
+
+ if (d->cacheDirectory.isEmpty()) {
+ qWarning() << "QNetworkDiskCache::prepare() The cache directory is not set";
+ return 0;
+ }
+
+ foreach (QNetworkCacheMetaData::RawHeader header, metaData.rawHeaders()) {
+ if (header.first.toLower() == "content-length") {
+ qint64 size = header.second.toInt();
+ if (size > (maximumCacheSize() * 3)/4)
+ return 0;
+ break;
+ }
+ }
+ QScopedPointer<QCacheItem> cacheItem(new QCacheItem);
+ cacheItem->metaData = metaData;
+
+ QIODevice *device = 0;
+ if (cacheItem->canCompress()) {
+ cacheItem->data.open(QBuffer::ReadWrite);
+ device = &(cacheItem->data);
+ } else {
+ QString templateName = d->tmpCacheFileName();
+ QT_TRY {
+ cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
+ } QT_CATCH(...) {
+ cacheItem->file = 0;
+ }
+ if (!cacheItem->file || !cacheItem->file->open()) {
+ qWarning() << "QNetworkDiskCache::prepare() unable to open temporary file";
+ cacheItem.reset();
+ return 0;
+ }
+ cacheItem->writeHeader(cacheItem->file);
+ device = cacheItem->file;
+ }
+ d->inserting[device] = cacheItem.take();
+ return device;
+}
+
+/*!
+ \reimp
+*/
+void QNetworkDiskCache::insert(QIODevice *device)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::insert()" << device;
+#endif
+ Q_D(QNetworkDiskCache);
+ QHash<QIODevice*, QCacheItem*>::iterator it = d->inserting.find(device);
+ if (it == d->inserting.end()) {
+ qWarning() << "QNetworkDiskCache::insert() called on a device we don't know about" << device;
+ return;
+ }
+
+ d->storeItem(it.value());
+ delete it.value();
+ 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);
+ Q_ASSERT(cacheItem->metaData.saveToDisk());
+
+ QString fileName = cacheFileName(cacheItem->metaData.url());
+ Q_ASSERT(!fileName.isEmpty());
+
+ if (QFile::exists(fileName)) {
+ if (!QFile::remove(fileName)) {
+ qWarning() << "QNetworkDiskCache: couldn't remove the cache file " << fileName;
+ return;
+ }
+ }
+
+ if (currentCacheSize > 0)
+ currentCacheSize += 1024 + cacheItem->size();
+ currentCacheSize = q->expire();
+ if (!cacheItem->file) {
+ QString templateName = tmpCacheFileName();
+ cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
+ if (cacheItem->file->open()) {
+ cacheItem->writeHeader(cacheItem->file);
+ cacheItem->writeCompressedData(cacheItem->file);
+ }
+ }
+
+ if (cacheItem->file
+ && cacheItem->file->isOpen()
+ && cacheItem->file->error() == QFile::NoError) {
+ cacheItem->file->setAutoRemove(false);
+ // ### use atomic rename rather then remove & rename
+ if (cacheItem->file->rename(fileName))
+ currentCacheSize += cacheItem->file->size();
+ else
+ cacheItem->file->setAutoRemove(true);
+ }
+ if (cacheItem->metaData.url() == lastItem.metaData.url())
+ lastItem.reset();
+}
+
+/*!
+ \reimp
+*/
+bool QNetworkDiskCache::remove(const QUrl &url)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::remove()" << url;
+#endif
+ Q_D(QNetworkDiskCache);
+
+ // remove is also used to cancel insertions, not a common operation
+ QHashIterator<QIODevice*, QCacheItem*> it(d->inserting);
+ while (it.hasNext()) {
+ it.next();
+ QCacheItem *item = it.value();
+ if (item && item->metaData.url() == url) {
+ delete item;
+ d->inserting.remove(it.key());
+ return true;
+ }
+ }
+
+ if (d->lastItem.metaData.url() == url)
+ d->lastItem.reset();
+ return d->removeFile(d->cacheFileName(url));
+}
+
+/*!
+ Put all of the misc file removing into one function to be extra safe
+ */
+bool QNetworkDiskCachePrivate::removeFile(const QString &file)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::removFile()" << file;
+#endif
+ if (file.isEmpty())
+ return false;
+ QFileInfo info(file);
+ QString fileName = info.fileName();
+ if (!fileName.endsWith(CACHE_POSTFIX))
+ return false;
+ qint64 size = info.size();
+ if (QFile::remove(file)) {
+ currentCacheSize -= size;
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+QNetworkCacheMetaData QNetworkDiskCache::metaData(const QUrl &url)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::metaData()" << url;
+#endif
+ Q_D(QNetworkDiskCache);
+ if (d->lastItem.metaData.url() == url)
+ return d->lastItem.metaData;
+ return fileMetaData(d->cacheFileName(url));
+}
+
+/*!
+ Returns the QNetworkCacheMetaData for the cache file \a fileName.
+
+ If \a fileName is not a cache file QNetworkCacheMetaData will be invalid.
+ */
+QNetworkCacheMetaData QNetworkDiskCache::fileMetaData(const QString &fileName) const
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::fileMetaData()" << fileName;
+#endif
+ Q_D(const QNetworkDiskCache);
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly))
+ return QNetworkCacheMetaData();
+ if (!d->lastItem.read(&file, false)) {
+ file.close();
+ QNetworkDiskCachePrivate *that = const_cast<QNetworkDiskCachePrivate*>(d);
+ that->removeFile(fileName);
+ }
+ return d->lastItem.metaData;
+}
+
+/*!
+ \reimp
+*/
+QIODevice *QNetworkDiskCache::data(const QUrl &url)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::data()" << url;
+#endif
+ Q_D(QNetworkDiskCache);
+ QScopedPointer<QBuffer> buffer;
+ if (!url.isValid())
+ return 0;
+ if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
+ buffer.reset(new QBuffer);
+ buffer->setData(d->lastItem.data.data());
+ } else {
+ QScopedPointer<QFile> file(new QFile(d->cacheFileName(url)));
+ if (!file->open(QFile::ReadOnly | QIODevice::Unbuffered))
+ return 0;
+
+ if (!d->lastItem.read(file.data(), true)) {
+ file->close();
+ remove(url);
+ return 0;
+ }
+ if (d->lastItem.data.isOpen()) {
+ // compressed
+ buffer.reset(new QBuffer);
+ buffer->setData(d->lastItem.data.data());
+ } else {
+ buffer.reset(new QBuffer);
+ // ### verify that QFile uses the fd size and not the file name
+ qint64 size = file->size() - file->pos();
+ const uchar *p = 0;
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_INTEGRITY)
+ p = file->map(file->pos(), size);
+#endif
+ if (p) {
+ buffer->setData((const char *)p, size);
+ file.take()->setParent(buffer.data());
+ } else {
+ buffer->setData(file->readAll());
+ }
+ }
+ }
+ buffer->open(QBuffer::ReadOnly);
+ return buffer.take();
+}
+
+/*!
+ \reimp
+*/
+void QNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData &metaData)
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::updateMetaData()" << metaData.url();
+#endif
+ QUrl url = metaData.url();
+ QIODevice *oldDevice = data(url);
+ if (!oldDevice) {
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::updateMetaData(), no device!";
+#endif
+ return;
+ }
+
+ QIODevice *newDevice = prepare(metaData);
+ if (!newDevice) {
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::updateMetaData(), no new device!" << url;
+#endif
+ return;
+ }
+ char data[1024];
+ while (!oldDevice->atEnd()) {
+ qint64 s = oldDevice->read(data, 1024);
+ newDevice->write(data, s);
+ }
+ delete oldDevice;
+ insert(newDevice);
+}
+
+/*!
+ Returns the current maximum size for the disk cache.
+
+ \sa setMaximumCacheSize()
+ */
+qint64 QNetworkDiskCache::maximumCacheSize() const
+{
+ Q_D(const QNetworkDiskCache);
+ return d->maximumCacheSize;
+}
+
+/*!
+ Sets the maximum size of the disk cache to be \a size.
+
+ If the new size is smaller then the current cache size then the cache will call expire().
+
+ \sa maximumCacheSize()
+ */
+void QNetworkDiskCache::setMaximumCacheSize(qint64 size)
+{
+ Q_D(QNetworkDiskCache);
+ bool expireCache = (size < d->maximumCacheSize);
+ d->maximumCacheSize = size;
+ if (expireCache)
+ d->currentCacheSize = expire();
+}
+
+/*!
+ Cleans the cache so that its size is under the maximum cache size.
+ Returns the current size of the cache.
+
+ When the current size of the cache is greater than the maximumCacheSize()
+ older cache files are removed until the total size is less then 90% of
+ maximumCacheSize() starting with the oldest ones first using the file
+ creation date to determine how old a cache file is.
+
+ Subclasses can reimplement this function to change the order that cache
+ files are removed taking into account information in the application
+ knows about that QNetworkDiskCache does not, for example the number of times
+ a cache is accessed.
+
+ Note: cacheSize() calls expire if the current cache size is unknown.
+
+ \sa maximumCacheSize(), fileMetaData()
+ */
+qint64 QNetworkDiskCache::expire()
+{
+ Q_D(QNetworkDiskCache);
+ if (d->currentCacheSize >= 0 && d->currentCacheSize < maximumCacheSize())
+ return d->currentCacheSize;
+
+ if (cacheDirectory().isEmpty()) {
+ qWarning() << "QNetworkDiskCache::expire() The cache directory is not set";
+ 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);
+
+ QMultiMap<QDateTime, QString> cacheItems;
+ qint64 totalSize = 0;
+ while (it.hasNext()) {
+ QString path = it.next();
+ QFileInfo info = it.fileInfo();
+ QString fileName = info.fileName();
+ if (fileName.endsWith(CACHE_POSTFIX)) {
+ cacheItems.insert(info.created(), path);
+ totalSize += info.size();
+ }
+ }
+
+ int removedFiles = 0;
+ qint64 goal = (maximumCacheSize() * 9) / 10;
+ QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
+ while (i != cacheItems.constEnd()) {
+ if (totalSize < goal)
+ break;
+ QString name = i.value();
+ QFile file(name);
+ qint64 size = file.size();
+ file.remove();
+ totalSize -= size;
+ ++removedFiles;
+ ++i;
+ }
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ if (removedFiles > 0) {
+ qDebug() << "QNetworkDiskCache::expire()"
+ << "Removed:" << removedFiles
+ << "Kept:" << cacheItems.count() - removedFiles;
+ }
+#endif
+ return totalSize;
+}
+
+/*!
+ \reimp
+*/
+void QNetworkDiskCache::clear()
+{
+#if defined(QNETWORKDISKCACHE_DEBUG)
+ qDebug() << "QNetworkDiskCache::clear()";
+#endif
+ Q_D(QNetworkDiskCache);
+ qint64 size = d->maximumCacheSize;
+ d->maximumCacheSize = 0;
+ d->currentCacheSize = expire();
+ d->maximumCacheSize = size;
+}
+
+/*!
+ Given a URL, generates a unique enough filename (and subdirectory)
+ */
+QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
+{
+ QUrl cleanUrl = url;
+ cleanUrl.setPassword(QString());
+ cleanUrl.setFragment(QString());
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(cleanUrl.toEncoded());
+ // 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
+{
+ //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 fullpath = dataDirectory + uniqueFileName(url);
+ return fullpath;
+}
+
+/*!
+ We compress small text and JavaScript files.
+ */
+bool QCacheItem::canCompress() const
+{
+ bool sizeOk = false;
+ bool typeOk = false;
+ foreach (QNetworkCacheMetaData::RawHeader header, metaData.rawHeaders()) {
+ if (header.first.toLower() == "content-length") {
+ qint64 size = header.second.toLongLong();
+ if (size > MAX_COMPRESSION_SIZE)
+ return false;
+ else
+ sizeOk = true;
+ }
+
+ if (header.first.toLower() == "content-type") {
+ QByteArray type = header.second;
+ if (type.startsWith("text/")
+ || (type.startsWith("application/")
+ && (type.endsWith("javascript") || type.endsWith("ecmascript"))))
+ typeOk = true;
+ else
+ return false;
+ }
+ if (sizeOk && typeOk)
+ return true;
+ }
+ return false;
+}
+
+enum
+{
+ CacheMagic = 0xe8,
+ CurrentCacheVersion = CACHE_VERSION
+};
+
+void QCacheItem::writeHeader(QFile *device) const
+{
+ QDataStream out(device);
+
+ out << qint32(CacheMagic);
+ out << qint32(CurrentCacheVersion);
+ out << metaData;
+ bool compressed = canCompress();
+ out << compressed;
+}
+
+void QCacheItem::writeCompressedData(QFile *device) const
+{
+ QDataStream out(device);
+
+ out << qCompress(data.data());
+}
+
+/*!
+ Returns false if the file is a cache file,
+ but is an older version and should be removed otherwise true.
+ */
+bool QCacheItem::read(QFile *device, bool readData)
+{
+ reset();
+
+ QDataStream in(device);
+
+ qint32 marker;
+ qint32 v;
+ in >> marker;
+ in >> v;
+ if (marker != CacheMagic)
+ return true;
+
+ // If the cache magic is correct, but the version is not we should remove it
+ if (v != CurrentCacheVersion)
+ return false;
+
+ bool compressed;
+ QByteArray dataBA;
+ in >> metaData;
+ in >> compressed;
+ if (readData && compressed) {
+ in >> dataBA;
+ 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();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKDISKCACHE
diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h
new file mode 100644
index 0000000000..628f524b90
--- /dev/null
+++ b/src/network/access/qnetworkdiskcache.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKDISKCACHE_H
+#define QNETWORKDISKCACHE_H
+
+#include <QtNetwork/qabstractnetworkcache.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_NETWORKDISKCACHE
+
+class QNetworkDiskCachePrivate;
+class Q_NETWORK_EXPORT QNetworkDiskCache : public QAbstractNetworkCache
+{
+ Q_OBJECT
+
+public:
+ explicit QNetworkDiskCache(QObject *parent = 0);
+ ~QNetworkDiskCache();
+
+ QString cacheDirectory() const;
+ void setCacheDirectory(const QString &cacheDir);
+
+ qint64 maximumCacheSize() const;
+ void setMaximumCacheSize(qint64 size);
+
+ qint64 cacheSize() const;
+ QNetworkCacheMetaData metaData(const QUrl &url);
+ void updateMetaData(const QNetworkCacheMetaData &metaData);
+ QIODevice *data(const QUrl &url);
+ bool remove(const QUrl &url);
+ QIODevice *prepare(const QNetworkCacheMetaData &metaData);
+ void insert(QIODevice *device);
+
+ QNetworkCacheMetaData fileMetaData(const QString &fileName) const;
+
+public Q_SLOTS:
+ void clear();
+
+protected:
+ virtual qint64 expire();
+
+private:
+ Q_DECLARE_PRIVATE(QNetworkDiskCache)
+ Q_DISABLE_COPY(QNetworkDiskCache)
+};
+
+#endif // QT_NO_NETWORKDISKCACHE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QNETWORKDISKCACHE_H
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
new file mode 100644
index 0000000000..13db04fa1c
--- /dev/null
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKDISKCACHE_P_H
+#define QNETWORKDISKCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractnetworkcache_p.h"
+
+#include <qbuffer.h>
+#include <qhash.h>
+#include <qtemporaryfile.h>
+
+#ifndef QT_NO_NETWORKDISKCACHE
+
+QT_BEGIN_NAMESPACE
+
+class QFile;
+
+class QCacheItem
+{
+public:
+ QCacheItem() : file(0)
+ {
+ }
+ ~QCacheItem()
+ {
+ reset();
+ }
+
+ QNetworkCacheMetaData metaData;
+ QBuffer data;
+ QTemporaryFile *file;
+ inline qint64 size() const
+ { return file ? file->size() : data.size(); }
+
+ inline void reset() {
+ metaData = QNetworkCacheMetaData();
+ data.close();
+ delete file;
+ file = 0;
+ }
+ void writeHeader(QFile *device) const;
+ void writeCompressedData(QFile *device) const;
+ bool read(QFile *device, bool readData);
+
+ bool canCompress() const;
+};
+
+class QNetworkDiskCachePrivate : public QAbstractNetworkCachePrivate
+{
+public:
+ QNetworkDiskCachePrivate()
+ : QAbstractNetworkCachePrivate()
+ , maximumCacheSize(1024 * 1024 * 50)
+ , currentCacheSize(-1)
+ {}
+
+ 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;
+
+ QHash<QIODevice*, QCacheItem*> inserting;
+ Q_DECLARE_PUBLIC(QNetworkDiskCache)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKDISKCACHE
+
+#endif // QNETWORKDISKCACHE_P_H
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
new file mode 100644
index 0000000000..c176dde669
--- /dev/null
+++ b/src/network/access/qnetworkreply.cpp
@@ -0,0 +1,795 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include <QtNetwork/qsslconfiguration.h>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkReplyPrivate::QNetworkReplyPrivate()
+ : readBufferMaxSize(0),
+ operation(QNetworkAccessManager::UnknownOperation),
+ errorCode(QNetworkReply::NoError)
+ , isFinished(false)
+{
+ // set the default attribute values
+ attributes.insert(QNetworkRequest::ConnectionEncryptedAttribute, false);
+}
+
+
+/*!
+ \class QNetworkReply
+ \since 4.4
+ \brief The QNetworkReply class contains the data and headers for a request
+ sent with QNetworkAccessManager
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QNetworkReply class contains the data and meta data related to
+ a request posted with QNetworkAccessManager. Like QNetworkRequest,
+ it contains a URL and headers (both in parsed and raw form), some
+ information about the reply's state and the contents of the reply
+ itself.
+
+ QNetworkReply is a sequential-access QIODevice, which means that
+ once data is read from the object, it no longer kept by the
+ device. It is therefore the application's responsibility to keep
+ this data if it needs to. Whenever more data is received from the
+ network and processed, the readyRead() signal is emitted.
+
+ The downloadProgress() signal is also emitted when data is
+ received, but the number of bytes contained in it may not
+ represent the actual bytes received, if any transformation is done
+ to the contents (for example, decompressing and removing the
+ protocol overhead).
+
+ Even though QNetworkReply is a QIODevice connected to the contents
+ of the reply, it also emits the uploadProgress() signal, which
+ indicates the progress of the upload for operations that have such
+ content.
+
+ \note Do not delete the object in the slot connected to the
+ error() or finished() signal. Use deleteLater().
+
+ \sa QNetworkRequest, QNetworkAccessManager
+*/
+
+/*!
+ \enum QNetworkReply::NetworkError
+
+ Indicates all possible error conditions found during the
+ processing of the request.
+
+ \value NoError no error condition.
+ \note When the HTTP protocol returns a redirect no error will be
+ reported. You can check if there is a redirect with the
+ QNetworkRequest::RedirectionTargetAttribute attribute.
+
+ \value ConnectionRefusedError the remote server refused the
+ connection (the server is not accepting requests)
+
+ \value RemoteHostClosedError the remote server closed the
+ connection prematurely, before the entire reply was received and
+ processed
+
+ \value HostNotFoundError the remote host name was not found
+ (invalid hostname)
+
+ \value TimeoutError the connection to the remote server
+ timed out
+
+ \value OperationCanceledError the operation was canceled via calls
+ to abort() or close() before it was finished.
+
+ \value SslHandshakeFailedError the SSL/TLS handshake failed and the
+ encrypted channel could not be established. The sslErrors() signal
+ should have been emitted.
+
+ \value TemporaryNetworkFailureError the connection was broken due
+ to disconnection from the network, however the system has initiated
+ roaming to another access point. The request should be resubmitted
+ and will be processed as soon as the connection is re-established.
+
+ \value ProxyConnectionRefusedError the connection to the proxy
+ server was refused (the proxy server is not accepting requests)
+
+ \value ProxyConnectionClosedError the proxy server closed the
+ connection prematurely, before the entire reply was received and
+ processed
+
+ \value ProxyNotFoundError the proxy host name was not
+ found (invalid proxy hostname)
+
+ \value ProxyTimeoutError the connection to the proxy
+ timed out or the proxy did not reply in time to the request sent
+
+ \value ProxyAuthenticationRequiredError the proxy requires
+ authentication in order to honour the request but did not accept
+ any credentials offered (if any)
+
+ \value ContentAccessDenied the access to the remote
+ content was denied (similar to HTTP error 401)
+
+ \value ContentOperationNotPermittedError the operation requested
+ on the remote content is not permitted
+
+ \value ContentNotFoundError the remote content was not
+ found at the server (similar to HTTP error 404)
+
+ \value AuthenticationRequiredError the remote server requires
+ authentication to serve the content but the credentials provided
+ were not accepted (if any)
+
+ \value ContentReSendError the request needed to be sent
+ again, but this failed for example because the upload data
+ could not be read a second time.
+
+ \value ProtocolUnknownError the Network Access API cannot
+ honor the request because the protocol is not known
+
+ \value ProtocolInvalidOperationError the requested operation is
+ invalid for this protocol
+
+ \value UnknownNetworkError an unknown network-related
+ error was detected
+
+ \value UnknownProxyError an unknown proxy-related error
+ was detected
+
+ \value UnknownContentError an unknown error related to
+ the remote content was detected
+
+ \value ProtocolFailure a breakdown in protocol was
+ detected (parsing error, invalid or unexpected responses, etc.)
+
+ \sa error()
+*/
+
+/*!
+ \fn void QNetworkReply::sslErrors(const QList<QSslError> &errors)
+
+ This signal is emitted if the SSL/TLS session encountered errors
+ during the set up, including certificate verification errors. The
+ \a errors parameter contains the list of errors.
+
+ To indicate that the errors are not fatal and that the connection
+ should proceed, the ignoreSslErrors() function should be called
+ from the slot connected to this signal. If it is not called, the
+ SSL session will be torn down before any data is exchanged
+ (including the URL).
+
+ This signal can be used to display an error message to the user
+ indicating that security may be compromised and display the
+ SSL settings (see sslConfiguration() to obtain it). If the user
+ decides to proceed after analyzing the remote certificate, the
+ slot should call ignoreSslErrors().
+
+ \sa QSslSocket::sslErrors(), QNetworkAccessManager::sslErrors(),
+ sslConfiguration(), ignoreSslErrors()
+*/
+
+/*!
+ \fn void QNetworkReply::metaDataChanged()
+
+ \omit FIXME: Update name? \endomit
+
+ This signal is emitted whenever the metadata in this reply
+ changes. metadata is any information that is not the content
+ (data) itself, including the network headers. In the majority of
+ cases, the metadata will be known fully by the time the first
+ byte of data is received. However, it is possible to receive
+ updates of headers or other metadata during the processing of the
+ data.
+
+ \sa header(), rawHeaderList(), rawHeader(), hasRawHeader()
+*/
+
+/*!
+ \fn void QNetworkReply::finished()
+
+ This signal is emitted when the reply has finished
+ processing. After this signal is emitted, there will be no more
+ updates to the reply's data or metadata.
+
+ Unless close() has been called, the reply will be still be opened
+ for reading, so the data can be retrieved by calls to read() or
+ readAll(). In particular, if no calls to read() were made as a
+ result of readyRead(), a call to readAll() will retrieve the full
+ contents in a QByteArray.
+
+ This signal is emitted in tandem with
+ QNetworkAccessManager::finished() where that signal's reply
+ parameter is this object.
+
+ \note Do not delete the object in the slot connected to this
+ signal. Use deleteLater().
+
+ You can also use isFinished() to check if a QNetworkReply
+ has finished even before you receive the finished() signal.
+
+ \sa QNetworkAccessManager::finished(), isFinished()
+*/
+
+/*!
+ \fn void QNetworkReply::error(QNetworkReply::NetworkError code)
+
+ This signal is emitted when the reply detects an error in
+ processing. The finished() signal will probably follow, indicating
+ that the connection is over.
+
+ The \a code parameter contains the code of the error that was
+ detected. Call errorString() to obtain a textual representation of
+ the error condition.
+
+ \note Do not delete the object in the slot connected to this
+ signal. Use deleteLater().
+
+ \sa error(), errorString()
+*/
+
+/*!
+ \fn void QNetworkReply::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
+
+ This signal is emitted to indicate the progress of the upload part
+ of this network request, if there's any. If there's no upload
+ associated with this request, this signal will not be emitted.
+
+ The \a bytesSent
+ parameter indicates the number of bytes uploaded, while \a
+ bytesTotal indicates the total number of bytes to be uploaded. If
+ the number of bytes to be uploaded could not be determined, \a
+ bytesTotal will be -1.
+
+ The upload is finished when \a bytesSent is equal to \a
+ bytesTotal. At that time, \a bytesTotal will not be -1.
+
+ \sa downloadProgress()
+*/
+
+/*!
+ \fn void QNetworkReply::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
+
+ This signal is emitted to indicate the progress of the download
+ part of this network request, if there's any. If there's no
+ download associated with this request, this signal will be emitted
+ once with 0 as the value of both \a bytesReceived and \a
+ bytesTotal.
+
+ The \a bytesReceived parameter indicates the number of bytes
+ received, while \a bytesTotal indicates the total number of bytes
+ expected to be downloaded. If the number of bytes to be downloaded
+ is not known, \a bytesTotal will be -1.
+
+ The download is finished when \a bytesReceived is equal to \a
+ bytesTotal. At that time, \a bytesTotal will not be -1.
+
+ Note that the values of both \a bytesReceived and \a bytesTotal
+ may be different from size(), the total number of bytes
+ obtained through read() or readAll(), or the value of the
+ header(ContentLengthHeader). The reason for that is that there may
+ be protocol overhead or the data may be compressed during the
+ download.
+
+ \sa uploadProgress(), bytesAvailable()
+*/
+
+/*!
+ \fn void QNetworkReply::abort()
+
+ Aborts the operation immediately and close down any network
+ connections still open. Uploads still in progress are also
+ aborted.
+
+ \sa close()
+*/
+
+/*!
+ Creates a QNetworkReply object with parent \a parent.
+
+ You cannot directly instantiate QNetworkReply objects. Use
+ QNetworkAccessManager functions to do that.
+*/
+QNetworkReply::QNetworkReply(QObject *parent)
+ : QIODevice(*new QNetworkReplyPrivate, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QNetworkReply::QNetworkReply(QNetworkReplyPrivate &dd, QObject *parent)
+ : QIODevice(dd, parent)
+{
+}
+
+/*!
+ Disposes of this reply and frees any resources associated with
+ it. If any network connections are still open, they will be
+ closed.
+
+ \sa abort(), close()
+*/
+QNetworkReply::~QNetworkReply()
+{
+}
+
+/*!
+ Closes this device for reading. Unread data is discarded, but the
+ network resources are not discarded until they are finished. In
+ particular, if any upload is in progress, it will continue until
+ it is done.
+
+ The finished() signal is emitted when all operations are over and
+ the network resources are freed.
+
+ \sa abort(), finished()
+*/
+void QNetworkReply::close()
+{
+ QIODevice::close();
+}
+
+/*!
+ \internal
+*/
+bool QNetworkReply::isSequential() const
+{
+ return true;
+}
+
+/*!
+ Returns the size of the read buffer, in bytes.
+
+ \sa setReadBufferSize()
+*/
+qint64 QNetworkReply::readBufferSize() const
+{
+ return d_func()->readBufferMaxSize;
+}
+
+/*!
+ Sets the size of the read buffer to be \a size bytes. The read
+ buffer is the buffer that holds data that is being downloaded off
+ the network, before it is read with QIODevice::read(). Setting the
+ buffer size to 0 will make the buffer unlimited in size.
+
+ QNetworkReply will try to stop reading from the network once this
+ buffer is full (i.e., bytesAvailable() returns \a size or more),
+ thus causing the download to throttle down as well. If the buffer
+ is not limited in size, QNetworkReply will try to download as fast
+ as possible from the network.
+
+ Unlike QAbstractSocket::setReadBufferSize(), QNetworkReply cannot
+ guarantee precision in the read buffer size. That is,
+ bytesAvailable() can return more than \a size.
+
+ \sa readBufferSize()
+*/
+void QNetworkReply::setReadBufferSize(qint64 size)
+{
+ Q_D(QNetworkReply);
+ d->readBufferMaxSize = size;
+}
+
+/*!
+ Returns the QNetworkAccessManager that was used to create this
+ QNetworkReply object. Initially, it is also the parent object.
+*/
+QNetworkAccessManager *QNetworkReply::manager() const
+{
+ return d_func()->manager;
+}
+
+/*!
+ Returns the request that was posted for this reply. In special,
+ note that the URL for the request may be different than that of
+ the reply.
+
+ \sa QNetworkRequest::url(), url(), setRequest()
+*/
+QNetworkRequest QNetworkReply::request() const
+{
+ return d_func()->request;
+}
+
+/*!
+ Returns the operation that was posted for this reply.
+
+ \sa setOperation()
+*/
+QNetworkAccessManager::Operation QNetworkReply::operation() const
+{
+ return d_func()->operation;
+}
+
+/*!
+ Returns the error that was found during the processing of this
+ request. If no error was found, returns NoError.
+
+ \sa setError()
+*/
+QNetworkReply::NetworkError QNetworkReply::error() const
+{
+ return d_func()->errorCode;
+}
+
+/*!
+ \since 4.6
+
+ Returns true when the reply has finished or was aborted.
+
+ \sa isRunning()
+*/
+bool QNetworkReply::isFinished() const
+{
+ return d_func()->isFinished;
+}
+
+/*!
+ \since 4.6
+
+ Returns true when the request is still processing and the
+ reply has not finished or was aborted yet.
+
+ \sa isFinished()
+*/
+bool QNetworkReply::isRunning() const
+{
+ return !isFinished();
+}
+
+/*!
+ Returns the URL of the content downloaded or uploaded. Note that
+ the URL may be different from that of the original request.
+
+ \sa request(), setUrl(), QNetworkRequest::url()
+*/
+QUrl QNetworkReply::url() const
+{
+ return d_func()->url;
+}
+
+/*!
+ Returns the value of the known header \a header, if that header
+ was sent by the remote server. If the header was not sent, returns
+ an invalid QVariant.
+
+ \sa rawHeader(), setHeader(), QNetworkRequest::header()
+*/
+QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const
+{
+ return d_func()->cookedHeaders.value(header);
+}
+
+/*!
+ Returns true if the raw header of name \a headerName was sent by
+ the remote server
+
+ \sa rawHeader()
+*/
+bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
+{
+ Q_D(const QNetworkReply);
+ return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
+}
+
+/*!
+ Returns the raw contents of the header \a headerName as sent by
+ the remote server. If there is no such header, returns an empty
+ byte array, which may be indistinguishable from an empty
+ header. Use hasRawHeader() to verify if the server sent such
+ header field.
+
+ \sa setRawHeader(), hasRawHeader(), header()
+*/
+QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
+{
+ Q_D(const QNetworkReply);
+ QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
+ d->findRawHeader(headerName);
+ if (it != d->rawHeaders.constEnd())
+ return it->second;
+ return QByteArray();
+}
+
+/*! \typedef QNetworkReply::RawHeaderPair
+
+ RawHeaderPair is a QPair<QByteArray, QByteArray> where the first
+ QByteArray is the header name and the second is the header.
+ */
+
+/*!
+ Returns a list of raw header pairs.
+ */
+const QList<QNetworkReply::RawHeaderPair>& QNetworkReply::rawHeaderPairs() const
+{
+ Q_D(const QNetworkReply);
+ return d->rawHeaders;
+}
+
+/*!
+ Returns a list of headers fields that were sent by the remote
+ server, in the order that they were sent. Duplicate headers are
+ merged together and take place of the latter duplicate.
+*/
+QList<QByteArray> QNetworkReply::rawHeaderList() const
+{
+ return d_func()->rawHeadersKeys();
+}
+
+/*!
+ Returns the attribute associated with the code \a code. If the
+ attribute has not been set, it returns an invalid QVariant (type QVariant::Null).
+
+ You can expect the default values listed in
+ QNetworkRequest::Attribute to be applied to the values returned by
+ this function.
+
+ \sa setAttribute(), QNetworkRequest::Attribute
+*/
+QVariant QNetworkReply::attribute(QNetworkRequest::Attribute code) const
+{
+ return d_func()->attributes.value(code);
+}
+
+#ifndef QT_NO_OPENSSL
+/*!
+ Returns the SSL configuration and state associated with this
+ reply, if SSL was used. It will contain the remote server's
+ certificate, its certificate chain leading to the Certificate
+ Authority as well as the encryption ciphers in use.
+
+ The peer's certificate and its certificate chain will be known by
+ the time sslErrors() is emitted, if it's emitted.
+*/
+QSslConfiguration QNetworkReply::sslConfiguration() const
+{
+ QSslConfiguration config;
+
+ // determine if we support this extension
+ int id = metaObject()->indexOfMethod("sslConfigurationImplementation()");
+ if (id != -1) {
+ void *arr[] = { &config, 0 };
+ const_cast<QNetworkReply *>(this)->qt_metacall(QMetaObject::InvokeMetaMethod, id, arr);
+ }
+ return config;
+}
+
+/*!
+ Sets the SSL configuration for the network connection associated
+ with this request, if possible, to be that of \a config.
+*/
+void QNetworkReply::setSslConfiguration(const QSslConfiguration &config)
+{
+ if (config.isNull())
+ return;
+
+ int id = metaObject()->indexOfMethod("setSslConfigurationImplementation(QSslConfiguration)");
+ if (id != -1) {
+ QSslConfiguration copy(config);
+ void *arr[] = { 0, &copy };
+ qt_metacall(QMetaObject::InvokeMetaMethod, id, arr);
+ }
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ If this function is called, the SSL errors given in \a errors
+ will be ignored.
+
+ Note that you can set the expected certificate in the SSL error:
+ If, for instance, you want to issue a request to a server that uses
+ a self-signed certificate, consider the following snippet:
+
+ \snippet doc/src/snippets/code/src_network_access_qnetworkreply.cpp 0
+
+ Multiple calls to this function will replace the list of errors that
+ were passed in previous calls.
+ You can clear the list of errors you want to ignore by calling this
+ function with an empty list.
+
+ \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
+*/
+void QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ // do this cryptic trick, because we could not add a virtual method to this class later on
+ // since that breaks binary compatibility
+ int id = metaObject()->indexOfMethod("ignoreSslErrorsImplementation(QList<QSslError>)");
+ if (id != -1) {
+ QList<QSslError> copy(errors);
+ void *arr[] = { 0, &copy };
+ qt_metacall(QMetaObject::InvokeMetaMethod, id, arr);
+ }
+}
+#endif
+
+/*!
+ If this function is called, SSL errors related to network
+ connection will be ignored, including certificate validation
+ errors.
+
+ Note that calling this function without restraint may pose a
+ security risk for your application. Use it with care.
+
+ This function can be called from the slot connected to the
+ sslErrors() signal, which indicates which errors were
+ found.
+
+ \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
+*/
+void QNetworkReply::ignoreSslErrors()
+{
+}
+
+/*!
+ \internal
+*/
+qint64 QNetworkReply::writeData(const char *, qint64)
+{
+ return -1; // you can't write
+}
+
+/*!
+ Sets the associated operation for this object to be \a
+ operation. This value will be returned by operation().
+
+ Note: the operation should be set when this object is created and
+ not changed again.
+
+ \sa operation(), setRequest()
+*/
+void QNetworkReply::setOperation(QNetworkAccessManager::Operation operation)
+{
+ Q_D(QNetworkReply);
+ d->operation = operation;
+}
+
+/*!
+ Sets the associated request for this object to be \a request. This
+ value will be returned by request().
+
+ Note: the request should be set when this object is created and
+ not changed again.
+
+ \sa request(), setOperation()
+*/
+void QNetworkReply::setRequest(const QNetworkRequest &request)
+{
+ Q_D(QNetworkReply);
+ d->request = request;
+}
+
+/*!
+ Sets the error condition to be \a errorCode. The human-readable
+ message is set with \a errorString.
+
+ Calling setError() does not emit the error(QNetworkReply::NetworkError)
+ signal.
+
+ \sa error(), errorString()
+*/
+void QNetworkReply::setError(NetworkError errorCode, const QString &errorString)
+{
+ Q_D(QNetworkReply);
+ d->errorCode = errorCode;
+ setErrorString(errorString); // in QIODevice
+}
+
+/*!
+ \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
+ absolute or canonical).
+
+ \sa url(), request(), QNetworkRequest::url()
+*/
+void QNetworkReply::setUrl(const QUrl &url)
+{
+ Q_D(QNetworkReply);
+ d->url = url;
+}
+
+/*!
+ Sets the known header \a header to be of value \a value. The
+ corresponding raw form of the header will be set as well.
+
+ \sa header(), setRawHeader(), QNetworkRequest::setHeader()
+*/
+void QNetworkReply::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+{
+ Q_D(QNetworkReply);
+ d->setCookedHeader(header, value);
+}
+
+/*!
+ Sets the raw header \a headerName to be of value \a value. If \a
+ headerName was previously set, it is overridden. Multiple HTTP
+ headers of the same name are functionally equivalent to one single
+ header with the values concatenated, separated by commas.
+
+ If \a headerName matches a known header, the value \a value will
+ be parsed and the corresponding parsed form will also be set.
+
+ \sa rawHeader(), header(), setHeader(), QNetworkRequest::setRawHeader()
+*/
+void QNetworkReply::setRawHeader(const QByteArray &headerName, const QByteArray &value)
+{
+ Q_D(QNetworkReply);
+ d->setRawHeader(headerName, value);
+}
+
+/*!
+ Sets the attribute \a code to have value \a value. If \a code was
+ previously set, it will be overridden. If \a value is an invalid
+ QVariant, the attribute will be unset.
+
+ \sa attribute(), QNetworkRequest::setAttribute()
+*/
+void QNetworkReply::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
+{
+ Q_D(QNetworkReply);
+ if (value.isValid())
+ d->attributes.insert(code, value);
+ else
+ d->attributes.remove(code);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
new file mode 100644
index 0000000000..3a511cc7a9
--- /dev/null
+++ b/src/network/access/qnetworkreply.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLY_H
+#define QNETWORKREPLY_H
+
+#include <QtCore/QIODevice>
+#include <QtCore/QString>
+#include <QtCore/QVariant>
+
+#include <QtNetwork/QNetworkRequest>
+#include <QtNetwork/QNetworkAccessManager>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QUrl;
+class QVariant;
+class QAuthenticator;
+class QSslConfiguration;
+class QSslError;
+
+class QNetworkReplyPrivate;
+class Q_NETWORK_EXPORT QNetworkReply: public QIODevice
+{
+ Q_OBJECT
+ Q_ENUMS(NetworkError)
+public:
+ enum NetworkError {
+ NoError = 0,
+
+ // network layer errors [relating to the destination server] (1-99):
+ ConnectionRefusedError = 1,
+ RemoteHostClosedError,
+ HostNotFoundError,
+ TimeoutError,
+ OperationCanceledError,
+ SslHandshakeFailedError,
+ TemporaryNetworkFailureError,
+ UnknownNetworkError = 99,
+
+ // proxy errors (101-199):
+ ProxyConnectionRefusedError = 101,
+ ProxyConnectionClosedError,
+ ProxyNotFoundError,
+ ProxyTimeoutError,
+ ProxyAuthenticationRequiredError,
+ UnknownProxyError = 199,
+
+ // content errors (201-299):
+ ContentAccessDenied = 201,
+ ContentOperationNotPermittedError,
+ ContentNotFoundError,
+ AuthenticationRequiredError,
+ ContentReSendError,
+ UnknownContentError = 299,
+
+ // protocol errors
+ ProtocolUnknownError = 301,
+ ProtocolInvalidOperationError,
+ ProtocolFailure = 399
+ };
+
+ ~QNetworkReply();
+ virtual void abort() = 0;
+
+ // reimplemented from QIODevice
+ virtual void close();
+ virtual bool isSequential() const;
+
+ // like QAbstractSocket:
+ qint64 readBufferSize() const;
+ virtual void setReadBufferSize(qint64 size);
+
+ QNetworkAccessManager *manager() const;
+ QNetworkAccessManager::Operation operation() const;
+ QNetworkRequest request() const;
+ NetworkError error() const;
+ bool isFinished() const;
+ bool isRunning() const;
+ QUrl url() const;
+
+ // "cooked" headers
+ QVariant header(QNetworkRequest::KnownHeaders header) const;
+
+ // raw headers:
+ bool hasRawHeader(const QByteArray &headerName) const;
+ QList<QByteArray> rawHeaderList() const;
+ QByteArray rawHeader(const QByteArray &headerName) const;
+
+ typedef QPair<QByteArray, QByteArray> RawHeaderPair;
+ const QList<RawHeaderPair>& rawHeaderPairs() const;
+
+ // attributes
+ QVariant attribute(QNetworkRequest::Attribute code) const;
+
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration sslConfiguration() const;
+ void setSslConfiguration(const QSslConfiguration &configuration);
+ void ignoreSslErrors(const QList<QSslError> &errors);
+#endif
+
+public Q_SLOTS:
+ virtual void ignoreSslErrors();
+
+Q_SIGNALS:
+ void metaDataChanged();
+ void finished();
+ void error(QNetworkReply::NetworkError);
+#ifndef QT_NO_OPENSSL
+ void sslErrors(const QList<QSslError> &errors);
+#endif
+
+ void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+
+protected:
+ QNetworkReply(QObject *parent = 0);
+ QNetworkReply(QNetworkReplyPrivate &dd, QObject *parent);
+ virtual qint64 writeData(const char *data, qint64 len);
+
+ 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);
+ void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
+
+private:
+ Q_DECLARE_PRIVATE(QNetworkReply)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
new file mode 100644
index 0000000000..03cc6bd060
--- /dev/null
+++ b/src/network/access/qnetworkreply_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLY_P_H
+#define QNETWORKREPLY_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 "qnetworkrequest.h"
+#include "qnetworkrequest_p.h"
+#include "qnetworkreply.h"
+#include "QtCore/qpointer.h"
+#include "private/qiodevice_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkReplyPrivate: public QIODevicePrivate, public QNetworkHeadersPrivate
+{
+public:
+ QNetworkReplyPrivate();
+ QNetworkRequest request;
+ QUrl url;
+ QPointer<QNetworkAccessManager> manager;
+ qint64 readBufferMaxSize;
+ QNetworkAccessManager::Operation operation;
+ QNetworkReply::NetworkError errorCode;
+ bool isFinished;
+
+ static inline void setManager(QNetworkReply *reply, QNetworkAccessManager *manager)
+ { reply->d_func()->manager = manager; }
+
+ Q_DECLARE_PUBLIC(QNetworkReply)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
new file mode 100644
index 0000000000..a09ff5c9ff
--- /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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "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/qnetworkreplydataimpl_p.h b/src/network/access/qnetworkreplydataimpl_p.h
new file mode 100644
index 0000000000..2048376b44
--- /dev/null
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLYDATAIMPL_H
+#define QNETWORKREPLYDATAIMPL_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 "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkaccessmanager.h"
+#include <QBuffer>
+
+QT_BEGIN_NAMESPACE
+
+
+class QNetworkReplyDataImplPrivate;
+class QNetworkReplyDataImpl: public QNetworkReply
+{
+ Q_OBJECT
+public:
+ 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 qint64 readData(char *data, qint64 maxlen);
+
+ Q_DECLARE_PRIVATE(QNetworkReplyDataImpl)
+};
+
+class QNetworkReplyDataImplPrivate: public QNetworkReplyPrivate
+{
+public:
+ QNetworkReplyDataImplPrivate();
+ ~QNetworkReplyDataImplPrivate();
+
+ QPair<QString, QByteArray> decodeDataUrlResult;
+ QBuffer decodedData;
+
+ Q_DECLARE_PUBLIC(QNetworkReplyDataImpl)
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREPLYDATAIMPL_H
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
new file mode 100644
index 0000000000..babee32e08
--- /dev/null
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreplyfileimpl_p.h"
+
+#include "QtCore/qdatetime.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFileInfo>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
+ : QNetworkReplyPrivate(), realFileSize(0)
+{
+}
+
+QNetworkReplyFileImpl::~QNetworkReplyFileImpl()
+{
+}
+
+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);
+
+ QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
+
+ QUrl url = req.url();
+ if (url.host() == QLatin1String("localhost"))
+ url.setHost(QString());
+
+#if !defined(Q_OS_WIN)
+ // do not allow UNC paths on Unix
+ if (!url.host().isEmpty()) {
+ // we handle only local files
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
+ setError(QNetworkReply::ProtocolInvalidOperationError, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ return;
+ }
+#endif
+ if (url.path().isEmpty())
+ url.setPath(QLatin1String("/"));
+ setUrl(url);
+
+
+ QString fileName = url.toLocalFile();
+ if (fileName.isEmpty()) {
+ if (url.scheme() == QLatin1String("qrc"))
+ fileName = QLatin1Char(':') + url.path();
+ else
+ fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
+ }
+
+ QFileInfo fi(fileName);
+ if (fi.isDir()) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
+ setError(QNetworkReply::ContentOperationNotPermittedError, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ return;
+ }
+
+ 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(d->realFile.fileName(), d->realFile.errorString());
+
+ if (d->realFile.exists()) {
+ setError(QNetworkReply::ContentAccessDenied, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
+ } else {
+ setError(QNetworkReply::ContentNotFoundError, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
+ }
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ return;
+ }
+
+ setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
+ 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->realFileSize), Q_ARG(qint64, d->realFileSize));
+ QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+}
+void QNetworkReplyFileImpl::close()
+{
+ Q_D(QNetworkReplyFileImpl);
+ QNetworkReply::close();
+ d->realFile.close();
+}
+
+void QNetworkReplyFileImpl::abort()
+{
+ Q_D(QNetworkReplyFileImpl);
+ QNetworkReply::close();
+ d->realFile.close();
+}
+
+qint64 QNetworkReplyFileImpl::bytesAvailable() const
+{
+ Q_D(const QNetworkReplyFileImpl);
+ return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable();
+}
+
+bool QNetworkReplyFileImpl::isSequential () const
+{
+ return true;
+}
+
+qint64 QNetworkReplyFileImpl::size() const
+{
+ Q_D(const QNetworkReplyFileImpl);
+ return d->realFileSize;
+}
+
+/*!
+ \internal
+*/
+qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
+{
+ Q_D(QNetworkReplyFileImpl);
+ qint64 ret = d->realFile.read(data, maxlen);
+ if (ret == 0 && bytesAvailable() == 0)
+ return -1;
+ else
+ return ret;
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qnetworkreplyfileimpl_p.cpp"
+
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
new file mode 100644
index 0000000000..c5126de80a
--- /dev/null
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLYFILEIMPL_H
+#define QNETWORKREPLYFILEIMPL_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 "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkaccessmanager.h"
+#include <QFile>
+#include <QAbstractFileEngine>
+
+QT_BEGIN_NAMESPACE
+
+
+class QNetworkReplyFileImplPrivate;
+class QNetworkReplyFileImpl: public QNetworkReply
+{
+ Q_OBJECT
+public:
+ QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
+ ~QNetworkReplyFileImpl();
+ virtual void abort();
+
+ // reimplemented from QNetworkReply
+ virtual void close();
+ virtual qint64 bytesAvailable() const;
+ virtual bool isSequential () const;
+ qint64 size() const;
+
+ virtual qint64 readData(char *data, qint64 maxlen);
+
+ Q_DECLARE_PRIVATE(QNetworkReplyFileImpl)
+};
+
+class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate
+{
+public:
+ QNetworkReplyFileImplPrivate();
+
+ QFile realFile;
+ qint64 realFileSize;
+
+ Q_DECLARE_PUBLIC(QNetworkReplyFileImpl)
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREPLYFILEIMPL_H
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
new file mode 100644
index 0000000000..9eb505d1b7
--- /dev/null
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -0,0 +1,1087 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreplyimpl_p.h"
+#include "qnetworkaccessbackend_p.h"
+#include "qnetworkcookie.h"
+#include "qabstractnetworkcache.h"
+#include "QtCore/qcoreapplication.h"
+#include "QtCore/qdatetime.h"
+#include "QtNetwork/qsslconfiguration.h"
+#include "QtNetwork/qnetworksession.h"
+#include "qnetworkaccesshttpbackend_p.h"
+#include "qnetworkaccessmanager_p.h"
+
+#include <QtCore/QCoreApplication>
+
+Q_DECLARE_METATYPE(QSharedPointer<char>)
+
+QT_BEGIN_NAMESPACE
+
+inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
+ : 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 || state == Finished) {
+ qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
+ return;
+ }
+ state = Working;
+
+ // note: if that method is called directly, it cannot happen that the backend is 0,
+ // because we just checked via a qobject_cast that we got a http backend (see
+ // QNetworkReplyImplPrivate::setup())
+ if (!backend) {
+ error(QNetworkReplyImpl::ProtocolUnknownError,
+ QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
+ finished();
+ return;
+ }
+
+#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
+ // state changes.
+ state = WaitingForSession;
+
+ QNetworkSession *session = manager->d_func()->networkSession.data();
+
+ if (session) {
+ Q_Q(QNetworkReplyImpl);
+
+ QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)),
+ q, SLOT(_q_networkSessionFailed()));
+
+ if (!session->isOpen())
+ session->open();
+ } else {
+ qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
+ }
+
+ return;
+ }
+#endif
+
+ if (backend && backend->isSynchronous()) {
+ state = Finished;
+ q_func()->setFinished(true);
+ } else {
+ if (state != Finished) {
+ if (operation == QNetworkAccessManager::GetOperation)
+ pendingNotifications.append(NotifyDownstreamReadyWrite);
+
+ handleNotifications();
+ }
+ }
+}
+
+void QNetworkReplyImplPrivate::_q_copyReadyRead()
+{
+ Q_Q(QNetworkReplyImpl);
+ if (state != Working)
+ return;
+ 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)
+ // we'll be called again, eventually
+ break;
+
+ bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
+ QByteArray byteData;
+ byteData.resize(bytesToRead);
+ qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
+ if (bytesActuallyRead == -1) {
+ byteData.clear();
+ backendNotify(NotifyCopyFinished);
+ break;
+ }
+
+ byteData.resize(bytesActuallyRead);
+ readBuffer.append(byteData);
+
+ if (!copyDevice->isSequential() && copyDevice->atEnd()) {
+ backendNotify(NotifyCopyFinished);
+ bytesDownloaded += bytesActuallyRead;
+ break;
+ }
+
+ bytesDownloaded += bytesActuallyRead;
+ }
+
+ if (bytesDownloaded == lastBytesDownloaded) {
+ // we didn't read anything
+ return;
+ }
+
+ lastBytesDownloaded = bytesDownloaded;
+ QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ 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());
+ resumeNotificationHandling();
+}
+
+void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
+{
+ _q_copyReadyRead();
+}
+
+void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
+{
+ Q_Q(QNetworkReplyImpl);
+
+ // make sure this is only called once, ever.
+ //_q_bufferOutgoingData may call it or the readChannelFinished emission
+ if (state != Buffering)
+ return;
+
+ // disconnect signals
+ QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
+ QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
+
+ // finally, start the request
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+}
+
+void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
+{
+ Q_Q(QNetworkReplyImpl);
+
+ if (!outgoingDataBuffer) {
+ // first call, create our buffer
+ outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
+
+ QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
+ QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
+ }
+
+ qint64 bytesBuffered = 0;
+ qint64 bytesToBuffer = 0;
+
+ // read data into our buffer
+ forever {
+ bytesToBuffer = outgoingData->bytesAvailable();
+ // unknown? just try 2 kB, this also ensures we always try to read the EOF
+ if (bytesToBuffer <= 0)
+ bytesToBuffer = 2*1024;
+
+ char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
+ bytesBuffered = outgoingData->read(dst, bytesToBuffer);
+
+ if (bytesBuffered == -1) {
+ // EOF has been reached.
+ outgoingDataBuffer->chop(bytesToBuffer);
+
+ _q_bufferOutgoingDataFinished();
+ break;
+ } else if (bytesBuffered == 0) {
+ // nothing read right now, just wait until we get called again
+ outgoingDataBuffer->chop(bytesToBuffer);
+
+ break;
+ } else {
+ // don't break, try to read() again
+ outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
+ }
+ }
+}
+
+#ifndef QT_NO_BEARERMANAGEMENT
+void QNetworkReplyImplPrivate::_q_networkSessionConnected()
+{
+ Q_Q(QNetworkReplyImpl);
+
+ if (manager.isNull())
+ return;
+
+ QNetworkSession *session = manager->d_func()->networkSession.data();
+ if (!session)
+ return;
+
+ if (session->state() != QNetworkSession::Connected)
+ return;
+
+ switch (state) {
+ case QNetworkReplyImplPrivate::Buffering:
+ case QNetworkReplyImplPrivate::Working:
+ case QNetworkReplyImplPrivate::Reconnecting:
+ // Migrate existing downloads to new network connection.
+ migrateBackend();
+ break;
+ case QNetworkReplyImplPrivate::WaitingForSession:
+ // Start waiting requests.
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+ break;
+ default:
+ ;
+ }
+}
+
+void QNetworkReplyImplPrivate::_q_networkSessionFailed()
+{
+ // Abort waiting and working replies.
+ if (state == WaitingForSession || state == Working) {
+ state = Working;
+ error(QNetworkReplyImpl::UnknownNetworkError,
+ QCoreApplication::translate("QNetworkReply", "Network session error."));
+ finished();
+ }
+}
+#endif
+
+void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
+ QIODevice *data)
+{
+ Q_Q(QNetworkReplyImpl);
+
+ outgoingData = data;
+ request = req;
+ url = request.url();
+ operation = op;
+
+ q->QIODevice::open(QIODevice::ReadOnly);
+ // Internal code that does a HTTP reply for the synchronous Ajax
+ // in QtWebKit.
+ QVariant synchronousHttpAttribute = req.attribute(
+ 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.
+
+ if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
+ // backend does not need upload buffering or
+ // fixed size non-sequential
+ // just start the operation
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+ } else {
+ bool bufferingDisallowed =
+ req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
+ false).toBool();
+
+ if (bufferingDisallowed) {
+ // if a valid content-length header for the request was supplied, we can disable buffering
+ // if not, we will buffer anyway
+ if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+ } else {
+ state = Buffering;
+ QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
+ }
+ } else {
+ // _q_startOperation will be called when the buffering has finished.
+ state = Buffering;
+ QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
+ }
+ }
+ } else {
+ // for HTTP, we want to send out the request as fast as possible to the network, without
+ // invoking methods in a QueuedConnection
+#ifndef QT_NO_HTTP
+ if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
+ _q_startOperation();
+ } else {
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+ }
+#else
+ if (backend && backend->isSynchronous())
+ _q_startOperation();
+ else
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+#endif // QT_NO_HTTP
+ }
+}
+
+void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!pendingNotifications.contains(notification))
+ pendingNotifications.enqueue(notification);
+
+ if (pendingNotifications.size() == 1)
+ QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
+}
+
+void QNetworkReplyImplPrivate::handleNotifications()
+{
+ if (notificationHandlingPaused)
+ return;
+
+ NotificationQueue current = pendingNotifications;
+ pendingNotifications.clear();
+
+ if (state != Working)
+ return;
+
+ while (state == Working && !current.isEmpty()) {
+ InternalNotifications notification = current.dequeue();
+ switch (notification) {
+ case NotifyDownstreamReadyWrite:
+ if (copyDevice)
+ _q_copyReadyRead();
+ else
+ backend->downstreamReadyWrite();
+ break;
+
+ case NotifyCloseDownstreamChannel:
+ backend->closeDownstreamChannel();
+ break;
+
+ case NotifyCopyFinished: {
+ QIODevice *dev = copyDevice;
+ copyDevice = 0;
+ backend->copyFinished(dev);
+ break;
+ }
+ }
+ }
+}
+
+// Do not handle the notifications while we are emitting downloadProgress
+// or readyRead
+void QNetworkReplyImplPrivate::pauseNotificationHandling()
+{
+ notificationHandlingPaused = true;
+}
+
+// Resume notification handling
+void QNetworkReplyImplPrivate::resumeNotificationHandling()
+{
+ Q_Q(QNetworkReplyImpl);
+ notificationHandlingPaused = false;
+ if (pendingNotifications.size() >= 1)
+ QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
+}
+
+QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
+{
+ if (!backend)
+ return 0;
+ return backend->networkCache();
+}
+
+void QNetworkReplyImplPrivate::createCache()
+{
+ // check if we can save and if we're allowed to
+ if (!networkCache()
+ || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
+ || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferNetwork).toInt()
+ == QNetworkRequest::AlwaysNetwork)
+ return;
+ cacheEnabled = true;
+}
+
+bool QNetworkReplyImplPrivate::isCachingEnabled() const
+{
+ return (cacheEnabled && networkCache() != 0);
+}
+
+void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
+{
+ if (!enable && !cacheEnabled)
+ return; // nothing to do
+ if (enable && cacheEnabled)
+ return; // nothing to do either!
+
+ if (enable) {
+ if (bytesDownloaded) {
+ // refuse to enable in this case
+ qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
+ return;
+ }
+
+ createCache();
+ } else {
+ // someone told us to turn on, then back off?
+ // ok... but you should make up your mind
+ qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
+ "backend %s probably needs to be fixed",
+ backend->metaObject()->className());
+ networkCache()->remove(url);
+ cacheSaveDevice = 0;
+ cacheEnabled = false;
+ }
+}
+
+void QNetworkReplyImplPrivate::completeCacheSave()
+{
+ if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
+ networkCache()->remove(url);
+ } else if (cacheEnabled && cacheSaveDevice) {
+ networkCache()->insert(cacheSaveDevice);
+ }
+ cacheSaveDevice = 0;
+ cacheEnabled = false;
+}
+
+void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
+{
+ Q_Q(QNetworkReplyImpl);
+ bytesUploaded = bytesSent;
+ pauseNotificationHandling();
+ emit q->uploadProgress(bytesSent, bytesTotal);
+ resumeNotificationHandling();
+}
+
+
+qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
+{
+ enum { DesiredBufferSize = 32 * 1024 };
+ if (readBufferMaxSize == 0)
+ return DesiredBufferSize;
+
+ return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
+}
+
+void QNetworkReplyImplPrivate::initCacheSaveDevice()
+{
+ Q_Q(QNetworkReplyImpl);
+
+ // The disk cache does not support partial content, so don't even try to
+ // save any such content into the cache.
+ if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
+ cacheEnabled = false;
+ return;
+ }
+
+ // save the meta data
+ QNetworkCacheMetaData metaData;
+ metaData.setUrl(url);
+ metaData = backend->fetchCacheMetaData(metaData);
+
+ // save the redirect request also in the cache
+ QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirectionTarget.isValid()) {
+ QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
+ attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
+ metaData.setAttributes(attributes);
+ }
+
+ cacheSaveDevice = networkCache()->prepare(metaData);
+
+ if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
+ if (cacheSaveDevice && !cacheSaveDevice->isOpen())
+ qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
+ "class %s probably needs to be fixed",
+ networkCache()->metaObject()->className());
+
+ networkCache()->remove(url);
+ cacheSaveDevice = 0;
+ cacheEnabled = false;
+ }
+}
+
+// we received downstream data and send this to the cache
+// and to our readBuffer (which in turn gets read by the user of QNetworkReply)
+void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!q->isOpen())
+ return;
+
+ if (cacheEnabled && !cacheSaveDevice) {
+ initCacheSaveDevice();
+ }
+
+ qint64 bytesWritten = 0;
+ for (int i = 0; i < data.bufferCount(); i++) {
+ QByteArray const &item = data[i];
+
+ if (cacheSaveDevice)
+ cacheSaveDevice->write(item.constData(), item.size());
+ readBuffer.append(item);
+
+ bytesWritten += item.size();
+ }
+ data.clear();
+
+ bytesDownloaded += bytesWritten;
+ lastBytesDownloaded = bytesDownloaded;
+
+ appendDownstreamDataSignalEmissions();
+}
+
+void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
+{
+ Q_Q(QNetworkReplyImpl);
+
+ QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ if (preMigrationDownloaded != Q_INT64_C(-1))
+ totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+ pauseNotificationHandling();
+ // 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());
+
+ 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?
+void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
+{
+ Q_Q(QNetworkReplyImpl);
+ if (!q->isOpen())
+ return;
+
+ // read until EOF from data
+ if (copyDevice) {
+ qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
+ "backend probly needs to be fixed");
+ return;
+ }
+
+ copyDevice = data;
+ q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
+ q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
+
+ // start the copy:
+ _q_copyReadyRead();
+}
+
+void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
+{
+ Q_UNUSED(data)
+ // TODO implement
+
+ // TODO call
+
+ 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);
+
+ if (state == Finished || state == Aborted || state == WaitingForSession)
+ return;
+
+ pauseNotificationHandling();
+ QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ if (preMigrationDownloaded != Q_INT64_C(-1))
+ totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+
+ if (!manager.isNull()) {
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkSession *session = manager->d_func()->networkSession.data();
+ if (session && session->state() == QNetworkSession::Roaming &&
+ state == Working && errorCode != QNetworkReply::OperationCanceledError) {
+ // only content with a known size will fail with a temporary network failure error
+ if (!totalSize.isNull()) {
+ if (bytesDownloaded != totalSize) {
+ if (migrateBackend()) {
+ // either we are migrating or the request is finished/aborted
+ if (state == Reconnecting || state == WaitingForSession) {
+ resumeNotificationHandling();
+ return; // exit early if we are migrating.
+ }
+ } else {
+ error(QNetworkReply::TemporaryNetworkFailureError,
+ QNetworkReply::tr("Temporary network failure."));
+ }
+ }
+ }
+ }
+#endif
+ }
+ resumeNotificationHandling();
+
+ state = Finished;
+ q->setFinished(true);
+
+ pendingNotifications.clear();
+
+ pauseNotificationHandling();
+ if (totalSize.isNull() || totalSize == -1) {
+ emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
+ }
+
+ if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
+ emit q->uploadProgress(0, 0);
+ resumeNotificationHandling();
+
+ // if we don't know the total size of or we received everything save the cache
+ if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ completeCacheSave();
+
+ // note: might not be a good idea, since users could decide to delete us
+ // which would delete the backend too...
+ // maybe we should protect the backend
+ pauseNotificationHandling();
+ emit q->readChannelFinished();
+ emit q->finished();
+ resumeNotificationHandling();
+}
+
+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);
+
+ // note: might not be a good idea, since users could decide to delete us
+ // which would delete the backend too...
+ // maybe we should protect the backend
+ emit q->error(code);
+}
+
+void QNetworkReplyImplPrivate::metaDataChanged()
+{
+ Q_Q(QNetworkReplyImpl);
+ // 1. do we have cookies?
+ // 2. are we allowed to set them?
+ if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
+ && (static_cast<QNetworkRequest::LoadControl>
+ (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
+ QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
+ QList<QNetworkCookie> cookies =
+ qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
+ QNetworkCookieJar *jar = manager->cookieJar();
+ if (jar)
+ jar->setCookiesFromUrl(cookies, url);
+ }
+ emit q->metaDataChanged();
+}
+
+void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
+{
+ attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
+}
+
+void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
+{
+#ifndef QT_NO_OPENSSL
+ Q_Q(QNetworkReplyImpl);
+ emit q->sslErrors(errors);
+#else
+ Q_UNUSED(errors);
+#endif
+}
+
+QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
+ : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
+{
+}
+
+QNetworkReplyImpl::~QNetworkReplyImpl()
+{
+ Q_D(QNetworkReplyImpl);
+
+ // This code removes the data from the cache if it was prematurely aborted.
+ // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
+ // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
+ if (d->isCachingEnabled())
+ d->networkCache()->remove(url());
+}
+
+void QNetworkReplyImpl::abort()
+{
+ Q_D(QNetworkReplyImpl);
+ if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
+ return;
+
+ // stop both upload and download
+ if (d->outgoingData)
+ disconnect(d->outgoingData, 0, this, 0);
+ if (d->copyDevice)
+ disconnect(d->copyDevice, 0, this, 0);
+
+ QNetworkReply::close();
+
+ if (d->state != QNetworkReplyImplPrivate::Finished) {
+ // call finished which will emit signals
+ d->error(OperationCanceledError, tr("Operation canceled"));
+ d->finished();
+ }
+ d->state = QNetworkReplyImplPrivate::Aborted;
+
+ // finished may access the backend
+ if (d->backend) {
+ d->backend->deleteLater();
+ d->backend = 0;
+ }
+}
+
+void QNetworkReplyImpl::close()
+{
+ Q_D(QNetworkReplyImpl);
+ if (d->state == QNetworkReplyImplPrivate::Aborted ||
+ d->state == QNetworkReplyImplPrivate::Finished)
+ return;
+
+ // stop the download
+ if (d->backend)
+ d->backend->closeDownstreamChannel();
+ if (d->copyDevice)
+ disconnect(d->copyDevice, 0, this, 0);
+
+ QNetworkReply::close();
+
+ // call finished which will emit signals
+ d->error(OperationCanceledError, tr("Operation canceled"));
+ d->finished();
+}
+
+bool QNetworkReplyImpl::canReadLine () const
+{
+ Q_D(const QNetworkReplyImpl);
+ return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
+}
+
+
+/*!
+ Returns the number of bytes available for reading with
+ QIODevice::read(). The number of bytes available may grow until
+ the finished() signal is emitted.
+*/
+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();
+}
+
+void QNetworkReplyImpl::setReadBufferSize(qint64 size)
+{
+ Q_D(QNetworkReplyImpl);
+ if (size > d->readBufferMaxSize &&
+ size > d->readBuffer.byteAmount())
+ d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
+
+ QNetworkReply::setReadBufferSize(size);
+
+ if (d->backend)
+ d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
+}
+
+#ifndef QT_NO_OPENSSL
+QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
+{
+ Q_D(const QNetworkReplyImpl);
+ QSslConfiguration config;
+ if (d->backend)
+ d->backend->fetchSslConfiguration(config);
+ return config;
+}
+
+void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
+{
+ Q_D(QNetworkReplyImpl);
+ if (d->backend && !config.isNull())
+ d->backend->setSslConfiguration(config);
+}
+
+void QNetworkReplyImpl::ignoreSslErrors()
+{
+ Q_D(QNetworkReplyImpl);
+ if (d->backend)
+ d->backend->ignoreSslErrors();
+}
+
+void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
+{
+ Q_D(QNetworkReplyImpl);
+ if (d->backend)
+ d->backend->ignoreSslErrors(errors);
+}
+#endif // QT_NO_OPENSSL
+
+/*!
+ \internal
+*/
+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) {
+ // optimization for getChar()
+ *data = d->readBuffer.getChar();
+ return 1;
+ }
+
+ maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
+ return d->readBuffer.read(data, maxlen);
+}
+
+/*!
+ \internal Reimplemented for internal purposes
+*/
+bool QNetworkReplyImpl::event(QEvent *e)
+{
+ if (e->type() == QEvent::NetworkReplyUpdated) {
+ d_func()->handleNotifications();
+ return true;
+ }
+
+ return QObject::event(e);
+}
+
+/*
+ Migrates the backend of the QNetworkReply to a new network connection if required. Returns
+ true if the reply is migrated or it is not required; otherwise returns false.
+*/
+bool QNetworkReplyImplPrivate::migrateBackend()
+{
+ Q_Q(QNetworkReplyImpl);
+
+ // Network reply is already finished or aborted, don't need to migrate.
+ if (state == Finished || state == Aborted)
+ return true;
+
+ // Backend does not support resuming download.
+ if (!backend->canResume())
+ return false;
+
+ // Request has outgoing data, not migrating.
+ if (outgoingData)
+ return false;
+
+ // Request is serviced from the cache, don't need to migrate.
+ if (copyDevice)
+ return true;
+
+ state = QNetworkReplyImplPrivate::Reconnecting;
+
+ if (backend) {
+ delete backend;
+ backend = 0;
+ }
+
+ cookedHeaders.clear();
+ rawHeaders.clear();
+
+ preMigrationDownloaded = bytesDownloaded;
+
+ backend = manager->d_func()->findBackend(operation, request);
+
+ if (backend) {
+ backend->setParent(q);
+ backend->reply = this;
+ backend->setResumeOffset(bytesDownloaded);
+ }
+
+#ifndef QT_NO_HTTP
+ if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
+ _q_startOperation();
+ } else {
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+ }
+#else
+ QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+#endif // QT_NO_HTTP
+
+ return true;
+}
+
+#ifndef QT_NO_BEARERMANAGEMENT
+QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
+ const QNetworkRequest &req,
+ QNetworkAccessManager::Operation op)
+: QNetworkReply(parent)
+{
+ setRequest(req);
+ setUrl(req.url());
+ setOperation(op);
+
+ qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
+
+ QString msg = QCoreApplication::translate("QNetworkAccessManager",
+ "Network access is disabled.");
+ setError(UnknownNetworkError, msg);
+
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+}
+
+QDisabledNetworkReply::~QDisabledNetworkReply()
+{
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qnetworkreplyimpl_p.cpp"
+
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
new file mode 100644
index 0000000000..1a9ab7eda7
--- /dev/null
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLYIMPL_P_H
+#define QNETWORKREPLYIMPL_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 "qnetworkreply.h"
+#include "qnetworkreply_p.h"
+#include "qnetworkaccessmanager.h"
+#include "qnetworkproxy.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qqueue.h"
+#include "QtCore/qbuffer.h"
+#include "private/qringbuffer_p.h"
+#include "private/qbytedata_p.h"
+#include <QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractNetworkCache;
+class QNetworkAccessBackend;
+
+class QNetworkReplyImplPrivate;
+class QNetworkReplyImpl: public QNetworkReply
+{
+ Q_OBJECT
+public:
+ QNetworkReplyImpl(QObject *parent = 0);
+ ~QNetworkReplyImpl();
+ virtual void abort();
+
+ // reimplemented from QNetworkReply / QIODevice
+ virtual void close();
+ virtual qint64 bytesAvailable() const;
+ virtual void setReadBufferSize(qint64 size);
+ virtual bool canReadLine () const;
+
+ virtual qint64 readData(char *data, qint64 maxlen);
+ virtual bool event(QEvent *);
+
+#ifndef QT_NO_OPENSSL
+ Q_INVOKABLE QSslConfiguration sslConfigurationImplementation() const;
+ Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration);
+ virtual void ignoreSslErrors();
+ Q_INVOKABLE virtual void ignoreSslErrorsImplementation(const QList<QSslError> &errors);
+#endif
+
+ Q_DECLARE_PRIVATE(QNetworkReplyImpl)
+ Q_PRIVATE_SLOT(d_func(), void _q_startOperation())
+ Q_PRIVATE_SLOT(d_func(), void _q_copyReadyRead())
+ Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
+ Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
+#ifndef QT_NO_BEARERMANAGEMENT
+ Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected())
+ Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed())
+#endif
+};
+
+class QNetworkReplyImplPrivate: public QNetworkReplyPrivate
+{
+public:
+ enum InternalNotifications {
+ NotifyDownstreamReadyWrite,
+ NotifyCloseDownstreamChannel,
+ NotifyCopyFinished
+ };
+
+ enum State {
+ Idle, // The reply is idle.
+ Buffering, // The reply is buffering outgoing data.
+ Working, // The reply is uploading/downloading data.
+ Finished, // The reply has finished.
+ Aborted, // The reply has been aborted.
+ WaitingForSession, // The reply is waiting for the session to open before connecting.
+ Reconnecting // The reply will reconnect to once roaming has completed.
+ };
+
+ typedef QQueue<InternalNotifications> NotificationQueue;
+
+ QNetworkReplyImplPrivate();
+
+ void _q_startOperation();
+ void _q_sourceReadyRead();
+ void _q_sourceReadChannelFinished();
+ void _q_copyReadyRead();
+ void _q_copyReadChannelFinished();
+ void _q_bufferOutgoingData();
+ void _q_bufferOutgoingDataFinished();
+#ifndef QT_NO_BEARERMANAGEMENT
+ void _q_networkSessionConnected();
+ void _q_networkSessionFailed();
+#endif
+
+ void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
+ QIODevice *outgoingData);
+
+ void pauseNotificationHandling();
+ void resumeNotificationHandling();
+ void backendNotify(InternalNotifications notification);
+ void handleNotifications();
+ void createCache();
+ void completeCacheSave();
+
+ // callbacks from the backend (through the manager):
+ void setCachingEnabled(bool enable);
+ bool isCachingEnabled() const;
+ void consume(qint64 count);
+ void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal);
+ qint64 nextDownstreamBlockSize() const;
+
+ void initCacheSaveDevice();
+ void appendDownstreamDataSignalEmissions();
+ void appendDownstreamData(QByteDataBuffer &data);
+ 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);
+
+ QNetworkAccessBackend *backend;
+ QIODevice *outgoingData;
+ QSharedPointer<QRingBuffer> outgoingDataBuffer;
+ QIODevice *copyDevice;
+ QAbstractNetworkCache *networkCache() const;
+
+ bool migrateBackend();
+
+ bool cacheEnabled;
+ QIODevice *cacheSaveDevice;
+
+ NotificationQueue pendingNotifications;
+ bool notificationHandlingPaused;
+
+ QUrl urlForLastAuthentication;
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy lastProxyAuthentication;
+ QList<QNetworkProxy> proxyList;
+#endif
+
+ // Used for normal downloading. For "zero copy" the downloadBuffer is used
+ QByteDataBuffer readBuffer;
+ qint64 bytesDownloaded;
+ qint64 lastBytesDownloaded;
+ qint64 bytesUploaded;
+ qint64 preMigrationDownloaded;
+
+ QString httpReasonPhrase;
+ int httpStatusCode;
+
+ 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)
+};
+
+#ifndef QT_NO_BEARERMANAGEMENT
+class QDisabledNetworkReply : public QNetworkReply
+{
+ Q_OBJECT
+
+public:
+ QDisabledNetworkReply(QObject *parent, const QNetworkRequest &req,
+ QNetworkAccessManager::Operation op);
+ ~QDisabledNetworkReply();
+
+ void abort() { }
+protected:
+ qint64 readData(char *, qint64) { return -1; }
+};
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
new file mode 100644
index 0000000000..338969a909
--- /dev/null
+++ b/src/network/access/qnetworkrequest.cpp
@@ -0,0 +1,1032 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qnetworkrequest.h"
+#include "qnetworkcookie.h"
+#include "qnetworkrequest_p.h"
+#include "qsslconfiguration.h"
+#include "QtCore/qshareddata.h"
+#include "QtCore/qlocale.h"
+#include "QtCore/qdatetime.h"
+
+#include <ctype.h>
+#ifndef QT_NO_DATESTRING
+# include <stdio.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkRequest
+ \brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
+ \since 4.4
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ QNetworkRequest is part of the Network Access API and is the class
+ holding the information necessary to send a request over the
+ network. It contains a URL and some ancillary information that can
+ be used to modify the request.
+
+ \sa QNetworkReply, QNetworkAccessManager
+*/
+
+/*!
+ \enum QNetworkRequest::KnownHeaders
+
+ List of known header types that QNetworkRequest parses. Each known
+ header is also represented in raw form with its full HTTP name.
+
+ \value ContentTypeHeader corresponds to the HTTP Content-Type
+ header and contains a string containing the media (MIME) type and
+ any auxiliary data (for instance, charset)
+
+ \value ContentLengthHeader corresponds to the HTTP Content-Length
+ header and contains the length in bytes of the data transmitted.
+
+ \value LocationHeader corresponds to the HTTP Location
+ header and contains a URL representing the actual location of the
+ data, including the destination URL in case of redirections.
+
+ \value LastModifiedHeader corresponds to the HTTP Last-Modified
+ header and contains a QDateTime representing the last modification
+ date of the contents
+
+ \value CookieHeader corresponds to the HTTP Cookie header
+ and contains a QList<QNetworkCookie> representing the cookies to
+ be sent back to the server
+
+ \value SetCookieHeader corresponds to the HTTP Set-Cookie
+ header and contains a QList<QNetworkCookie> representing the
+ cookies sent by the server to be stored locally
+
+ \sa header(), setHeader(), rawHeader(), setRawHeader()
+*/
+
+/*!
+ \enum QNetworkRequest::Attribute
+ \since 4.7
+
+ Attribute codes for the QNetworkRequest and QNetworkReply.
+
+ Attributes are extra meta-data that are used to control the
+ behavior of the request and to pass further information from the
+ reply back to the application. Attributes are also extensible,
+ allowing custom implementations to pass custom values.
+
+ The following table explains what the default attribute codes are,
+ the QVariant types associated, the default value if said attribute
+ is missing and whether it's used in requests or replies.
+
+ \value HttpStatusCodeAttribute
+ Replies only, type: QVariant::Int (no default)
+ Indicates the HTTP status code received from the HTTP server
+ (like 200, 304, 404, 401, etc.). If the connection was not
+ HTTP-based, this attribute will not be present.
+
+ \value HttpReasonPhraseAttribute
+ Replies only, type: QVariant::ByteArray (no default)
+ Indicates the HTTP reason phrase as received from the HTTP
+ server (like "Ok", "Found", "Not Found", "Access Denied",
+ etc.) This is the human-readable representation of the status
+ code (see above). If the connection was not HTTP-based, this
+ attribute will not be present.
+
+ \value RedirectionTargetAttribute
+ Replies only, type: QVariant::Url (no default)
+ If present, it indicates that the server is redirecting the
+ request to a different URL. The Network Access API does not by
+ default follow redirections: it's up to the application to
+ determine if the requested redirection should be allowed,
+ according to its security policies.
+ The returned URL might be relative. Use QUrl::resolved()
+ to create an absolute URL out of it.
+
+ \value ConnectionEncryptedAttribute
+ Replies only, type: QVariant::Bool (default: false)
+ Indicates whether the data was obtained through an encrypted
+ (secure) connection.
+
+ \value CacheLoadControlAttribute
+ Requests only, type: QVariant::Int (default: QNetworkRequest::PreferNetwork)
+ Controls how the cache should be accessed. The possible values
+ are those of QNetworkRequest::CacheLoadControl. Note that the
+ default QNetworkAccessManager implementation does not support
+ caching. However, this attribute may be used by certain
+ backends to modify their requests (for example, for caching proxies).
+
+ \value CacheSaveControlAttribute
+ Requests only, type: QVariant::Bool (default: true)
+ Controls if the data obtained should be saved to cache for
+ future uses. If the value is false, the data obtained will not
+ be automatically cached. If true, data may be cached, provided
+ it is cacheable (what is cacheable depends on the protocol
+ being used).
+
+ \value SourceIsFromCacheAttribute
+ Replies only, type: QVariant::Bool (default: false)
+ Indicates whether the data was obtained from cache
+ or not.
+
+ \value DoNotBufferUploadDataAttribute
+ Requests only, type: QVariant::Bool (default: false)
+ Indicates whether the QNetworkAccessManager code is
+ allowed to buffer the upload data, e.g. when doing a HTTP POST.
+ When using this flag with sequential upload data, the ContentLengthHeader
+ header must be set.
+
+ \value HttpPipeliningAllowedAttribute
+ Requests only, type: QVariant::Bool (default: false)
+ Indicates whether the QNetworkAccessManager code is
+ allowed to use HTTP pipelining with this request.
+
+ \value HttpPipeliningWasUsedAttribute
+ Replies only, type: QVariant::Bool
+ Indicates whether the HTTP pipelining was used for receiving
+ this reply.
+
+ \value CustomVerbAttribute
+ Requests only, type: QVariant::ByteArray
+ Holds the value for the custom HTTP verb to send (destined for usage
+ of other verbs than GET, POST, PUT and DELETE). This verb is set
+ when calling QNetworkAccessManager::sendCustomRequest().
+
+ \value CookieLoadControlAttribute
+ Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic)
+ Indicates whether to send 'Cookie' headers in the request.
+ This attribute is set to false by QtWebKit when creating a cross-origin
+ XMLHttpRequest where withCredentials has not been set explicitly to true by the
+ Javascript that created the request.
+ See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag}{here} for more information.
+ (This value was introduced in 4.7.)
+
+ \value CookieSaveControlAttribute
+ Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic)
+ Indicates whether to save 'Cookie' headers received from the server in reply
+ to the request.
+ This attribute is set to false by QtWebKit when creating a cross-origin
+ XMLHttpRequest where withCredentials has not been set explicitly to true by the
+ Javascript that created the request.
+ See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
+ (This value was introduced in 4.7.)
+
+ \value AuthenticationReuseAttribute
+ Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic)
+ Indicates whether to use cached authorization credentials in the request,
+ if available. If this is set to QNetworkRequest::Manual and the authentication
+ mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP
+ header with any cached credentials it may have for the request's URL.
+ This attribute is set to QNetworkRequest::Manual by QtWebKit when creating a cross-origin
+ XMLHttpRequest where withCredentials has not been set explicitly to true by the
+ Javascript that created the request.
+ See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
+ (This value was introduced in 4.7.)
+
+ \omitvalue MaximumDownloadBufferSizeAttribute
+
+ \omitvalue DownloadBufferAttribute
+
+ \omitvalue SynchronousRequestAttribute
+
+ \value User
+ Special type. Additional information can be passed in
+ QVariants with types ranging from User to UserMax. The default
+ implementation of Network Access will ignore any request
+ attributes in this range and it will not produce any
+ attributes in this range in replies. The range is reserved for
+ extensions of QNetworkAccessManager.
+
+ \value UserMax
+ Special type. See User.
+*/
+
+/*!
+ \enum QNetworkRequest::CacheLoadControl
+
+ Controls the caching mechanism of QNetworkAccessManager.
+
+ \value AlwaysNetwork always load from network and do not
+ check if the cache has a valid entry (similar to the
+ "Reload" feature in browsers)
+
+ \value PreferNetwork default value; load from the network
+ if the cached entry is older than the network entry
+
+ \value PreferCache load from cache if available,
+ otherwise load from network. Note that this can return possibly
+ stale (but not expired) items from cache.
+
+ \value AlwaysCache only load from cache, indicating error
+ if the item was not cached (i.e., off-line mode)
+*/
+
+/*!
+ \enum QNetworkRequest::LoadControl
+ \since 4.7
+
+ Indicates if an aspect of the request's loading mechanism has been
+ manually overridden, e.g. by QtWebKit.
+
+ \value Automatic default value: indicates default behaviour.
+
+ \value Manual indicates behaviour has been manually overridden.
+*/
+
+class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
+{
+public:
+ inline QNetworkRequestPrivate()
+ : priority(QNetworkRequest::NormalPriority)
+#ifndef QT_NO_OPENSSL
+ , sslConfiguration(0)
+#endif
+ { qRegisterMetaType<QNetworkRequest>(); }
+ ~QNetworkRequestPrivate()
+ {
+#ifndef QT_NO_OPENSSL
+ delete sslConfiguration;
+#endif
+ }
+
+
+ QNetworkRequestPrivate(const QNetworkRequestPrivate &other)
+ : QSharedData(other), QNetworkHeadersPrivate(other)
+ {
+ url = other.url;
+ priority = other.priority;
+
+#ifndef QT_NO_OPENSSL
+ sslConfiguration = 0;
+ if (other.sslConfiguration)
+ sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
+#endif
+ }
+
+ inline bool operator==(const QNetworkRequestPrivate &other) const
+ {
+ return url == other.url &&
+ priority == other.priority &&
+ rawHeaders == other.rawHeaders &&
+ attributes == other.attributes;
+ // don't compare cookedHeaders
+ }
+
+ QUrl url;
+ QNetworkRequest::Priority priority;
+#ifndef QT_NO_OPENSSL
+ mutable QSslConfiguration *sslConfiguration;
+#endif
+};
+
+/*!
+ Constructs a QNetworkRequest object with \a url as the URL to be
+ requested.
+
+ \sa url(), setUrl()
+*/
+QNetworkRequest::QNetworkRequest(const QUrl &url)
+ : d(new QNetworkRequestPrivate)
+{
+ d->url = url;
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QNetworkRequest::QNetworkRequest(const QNetworkRequest &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Disposes of the QNetworkRequest object.
+*/
+QNetworkRequest::~QNetworkRequest()
+{
+ // QSharedDataPointer auto deletes
+ d = 0;
+}
+
+/*!
+ Returns true if this object is the same as \a other (i.e., if they
+ have the same URL, same headers and same meta-data settings).
+
+ \sa operator!=()
+*/
+bool QNetworkRequest::operator==(const QNetworkRequest &other) const
+{
+ return d == other.d || *d == *other.d;
+}
+
+/*!
+ \fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const
+
+ Returns false if this object is not the same as \a other.
+
+ \sa operator==()
+*/
+
+/*!
+ Creates a copy of \a other
+*/
+QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns the URL this network request is referring to.
+
+ \sa setUrl()
+*/
+QUrl QNetworkRequest::url() const
+{
+ return d->url;
+}
+
+/*!
+ Sets the URL this network request is referring to to be \a url.
+
+ \sa url()
+*/
+void QNetworkRequest::setUrl(const QUrl &url)
+{
+ d->url = url;
+}
+
+/*!
+ Returns the value of the known network header \a header if it is
+ present in this request. If it is not present, returns QVariant()
+ (i.e., an invalid variant).
+
+ \sa KnownHeaders, rawHeader(), setHeader()
+*/
+QVariant QNetworkRequest::header(KnownHeaders header) const
+{
+ return d->cookedHeaders.value(header);
+}
+
+/*!
+ Sets the value of the known header \a header to be \a value,
+ overriding any previously set headers. This operation also sets
+ the equivalent raw HTTP header.
+
+ \sa KnownHeaders, setRawHeader(), header()
+*/
+void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
+{
+ d->setCookedHeader(header, value);
+}
+
+/*!
+ Returns true if the raw header \a headerName is present in this
+ network request.
+
+ \sa rawHeader(), setRawHeader()
+*/
+bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
+{
+ return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
+}
+
+/*!
+ Returns the raw form of header \a headerName. If no such header is
+ present, an empty QByteArray is returned, which may be
+ indistinguishable from a header that is present but has no content
+ (use hasRawHeader() to find out if the header exists or not).
+
+ Raw headers can be set with setRawHeader() or with setHeader().
+
+ \sa header(), setRawHeader()
+*/
+QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
+{
+ QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
+ d->findRawHeader(headerName);
+ if (it != d->rawHeaders.constEnd())
+ return it->second;
+ return QByteArray();
+}
+
+/*!
+ Returns a list of all raw headers that are set in this network
+ request. The list is in the order that the headers were set.
+
+ \sa hasRawHeader(), rawHeader()
+*/
+QList<QByteArray> QNetworkRequest::rawHeaderList() const
+{
+ return d->rawHeadersKeys();
+}
+
+/*!
+ 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.
+
+ For example:
+ \snippet doc/src/snippets/code/src_network_access_qnetworkrequest.cpp 0
+
+ will also set the known header LastModifiedHeader to be the
+ QDateTime object of the parsed date.
+
+ 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 KnownHeaders, setHeader(), hasRawHeader(), rawHeader()
+*/
+void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
+{
+ d->setRawHeader(headerName, headerValue);
+}
+
+/*!
+ Returns the attribute associated with the code \a code. If the
+ attribute has not been set, it returns \a defaultValue.
+
+ Note: this function does not apply the defaults listed in
+ QNetworkRequest::Attribute.
+
+ \sa setAttribute(), QNetworkRequest::Attribute
+*/
+QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const
+{
+ return d->attributes.value(code, defaultValue);
+}
+
+/*!
+ Sets the attribute associated with code \a code to be value \a
+ value. If the attribute is already set, the previous value is
+ discarded. In special, if \a value is an invalid QVariant, the
+ attribute is unset.
+
+ \sa attribute(), QNetworkRequest::Attribute
+*/
+void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
+{
+ if (value.isValid())
+ d->attributes.insert(code, value);
+ else
+ d->attributes.remove(code);
+}
+
+#ifndef QT_NO_OPENSSL
+/*!
+ Returns this network request's SSL configuration. By default, no
+ SSL settings are specified.
+
+ \sa setSslConfiguration()
+*/
+QSslConfiguration QNetworkRequest::sslConfiguration() const
+{
+ if (!d->sslConfiguration)
+ d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
+ return *d->sslConfiguration;
+}
+
+/*!
+ Sets this network request's SSL configuration to be \a config. The
+ settings that apply are the private key, the local certificate,
+ the SSL protocol (SSLv2, SSLv3, TLSv1 where applicable), the CA
+ certificates and the ciphers that the SSL backend is allowed to
+ use.
+
+ By default, no SSL configuration is set, which allows the backends
+ to choose freely what configuration is best for them.
+
+ \sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
+*/
+void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config)
+{
+ if (!d->sslConfiguration)
+ d->sslConfiguration = new QSslConfiguration(config);
+ else
+ *d->sslConfiguration = config;
+}
+#endif
+
+/*!
+ \since 4.6
+
+ Allows setting a reference to the \a object initiating
+ the request.
+
+ For example QtWebKit sets the originating object to the
+ QWebFrame that initiated the request.
+
+ \sa originatingObject()
+*/
+void QNetworkRequest::setOriginatingObject(QObject *object)
+{
+ d->originatingObject = object;
+}
+
+/*!
+ \since 4.6
+
+ Returns a reference to the object that initiated this
+ network request; returns 0 if not set or the object has
+ been destroyed.
+
+ \sa setOriginatingObject()
+*/
+QObject *QNetworkRequest::originatingObject() const
+{
+ return d->originatingObject.data();
+}
+
+/*!
+ \since 4.7
+
+ Return the priority of this request.
+
+ \sa setPriority()
+*/
+QNetworkRequest::Priority QNetworkRequest::priority() const
+{
+ return d->priority;
+}
+
+/*! \enum QNetworkRequest::Priority
+
+ \since 4.7
+
+ This enum lists the possible network request priorities.
+
+ \value HighPriority High priority
+ \value NormalPriority Normal priority
+ \value LowPriority Low priority
+ */
+
+/*!
+ \since 4.7
+
+ Set the priority of this request to \a priority.
+
+ \note The \a priority is only a hint to the network access
+ manager. It can use it or not. Currently it is used for HTTP to
+ decide which request should be sent first to a server.
+
+ \sa priority()
+*/
+void QNetworkRequest::setPriority(Priority priority)
+{
+ d->priority = priority;
+}
+
+static QByteArray headerName(QNetworkRequest::KnownHeaders header)
+{
+ switch (header) {
+ case QNetworkRequest::ContentTypeHeader:
+ return "Content-Type";
+
+ case QNetworkRequest::ContentLengthHeader:
+ return "Content-Length";
+
+ case QNetworkRequest::LocationHeader:
+ return "Location";
+
+ case QNetworkRequest::LastModifiedHeader:
+ return "Last-Modified";
+
+ case QNetworkRequest::CookieHeader:
+ return "Cookie";
+
+ 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
+ }
+
+ return QByteArray();
+}
+
+static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
+{
+ switch (header) {
+ case QNetworkRequest::ContentTypeHeader:
+ case QNetworkRequest::ContentLengthHeader:
+ case QNetworkRequest::ContentDispositionHeader:
+ return value.toByteArray();
+
+ case QNetworkRequest::LocationHeader:
+ switch (value.type()) {
+ case QVariant::Url:
+ return value.toUrl().toEncoded();
+
+ default:
+ return value.toByteArray();
+ }
+
+ case QNetworkRequest::LastModifiedHeader:
+ switch (value.type()) {
+ case QVariant::Date:
+ case QVariant::DateTime:
+ // generate RFC 1123/822 dates:
+ return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
+
+ default:
+ return value.toByteArray();
+ }
+
+ case QNetworkRequest::CookieHeader: {
+ QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
+ if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
+ cookies << qvariant_cast<QNetworkCookie>(value);
+
+ QByteArray result;
+ bool first = true;
+ foreach (const QNetworkCookie &cookie, cookies) {
+ if (!first)
+ result += "; ";
+ first = false;
+ result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
+ }
+ return result;
+ }
+
+ case QNetworkRequest::SetCookieHeader: {
+ QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
+ if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
+ cookies << qvariant_cast<QNetworkCookie>(value);
+
+ QByteArray result;
+ bool first = true;
+ foreach (const QNetworkCookie &cookie, cookies) {
+ if (!first)
+ result += ", ";
+ first = false;
+ result += cookie.toRawForm(QNetworkCookie::Full);
+ }
+ return result;
+ }
+ }
+
+ return QByteArray();
+}
+
+static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerName)
+{
+ // headerName is not empty here
+
+ switch (tolower(headerName.at(0))) {
+ case 'c':
+ if (qstricmp(headerName.constData(), "content-type") == 0)
+ return QNetworkRequest::ContentTypeHeader;
+ else if (qstricmp(headerName.constData(), "content-length") == 0)
+ return QNetworkRequest::ContentLengthHeader;
+ else if (qstricmp(headerName.constData(), "cookie") == 0)
+ return QNetworkRequest::CookieHeader;
+ break;
+
+ case 'l':
+ if (qstricmp(headerName.constData(), "location") == 0)
+ return QNetworkRequest::LocationHeader;
+ else if (qstricmp(headerName.constData(), "last-modified") == 0)
+ return QNetworkRequest::LastModifiedHeader;
+ break;
+
+ case 's':
+ if (qstricmp(headerName.constData(), "set-cookie") == 0)
+ return QNetworkRequest::SetCookieHeader;
+ break;
+ }
+
+ return QNetworkRequest::KnownHeaders(-1); // nothing found
+}
+
+static QVariant parseHttpDate(const QByteArray &raw)
+{
+ QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
+ if (dt.isValid())
+ return dt;
+ return QVariant(); // transform an invalid QDateTime into a null QVariant
+}
+
+static QVariant parseCookieHeader(const QByteArray &raw)
+{
+ QList<QNetworkCookie> result;
+ QList<QByteArray> cookieList = raw.split(';');
+ foreach (const QByteArray &cookie, cookieList) {
+ QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
+ if (parsed.count() != 1)
+ return QVariant(); // invalid Cookie: header
+
+ result += parsed;
+ }
+
+ return QVariant::fromValue(result);
+}
+
+static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
+{
+ // header is always a valid value
+ switch (header) {
+ case QNetworkRequest::ContentTypeHeader:
+ // copy exactly, convert to QString
+ return QString::fromLatin1(value);
+
+ case QNetworkRequest::ContentLengthHeader: {
+ bool ok;
+ qint64 result = value.trimmed().toLongLong(&ok);
+ if (ok)
+ return result;
+ return QVariant();
+ }
+
+ case QNetworkRequest::LocationHeader: {
+ QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode);
+ if (result.isValid() && !result.scheme().isEmpty())
+ return result;
+ return QVariant();
+ }
+
+ case QNetworkRequest::LastModifiedHeader:
+ return parseHttpDate(value);
+
+ case QNetworkRequest::CookieHeader:
+ return parseCookieHeader(value);
+
+ case QNetworkRequest::SetCookieHeader:
+ return QVariant::fromValue(QNetworkCookie::parseCookies(value));
+
+ default:
+ Q_ASSERT(0);
+ }
+ return QVariant();
+}
+
+QNetworkHeadersPrivate::RawHeadersList::ConstIterator
+QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
+{
+ RawHeadersList::ConstIterator it = rawHeaders.constBegin();
+ RawHeadersList::ConstIterator end = rawHeaders.constEnd();
+ for ( ; it != end; ++it)
+ if (qstricmp(it->first.constData(), key.constData()) == 0)
+ return it;
+
+ return end; // not found
+}
+
+QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
+{
+ return rawHeaders;
+}
+
+QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
+{
+ QList<QByteArray> result;
+ RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
+ end = rawHeaders.constEnd();
+ for ( ; it != end; ++it)
+ result << it->first;
+
+ return result;
+}
+
+void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
+{
+ if (key.isEmpty())
+ // refuse to accept an empty raw header
+ return;
+
+ setRawHeaderInternal(key, value);
+ parseAndSetHeader(key, value);
+}
+
+/*!
+ \internal
+ Sets the internal raw headers list to match \a list. The cooked headers
+ will also be updated.
+
+ If \a list contains duplicates, they will be stored, but only the first one
+ is usually accessed.
+*/
+void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
+{
+ cookedHeaders.clear();
+ rawHeaders = list;
+
+ RawHeadersList::ConstIterator it = rawHeaders.constBegin();
+ RawHeadersList::ConstIterator end = rawHeaders.constEnd();
+ for ( ; it != end; ++it)
+ parseAndSetHeader(it->first, it->second);
+}
+
+void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
+ const QVariant &value)
+{
+ QByteArray name = headerName(header);
+ if (name.isEmpty()) {
+ // headerName verifies that \a header is a known value
+ qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
+ return;
+ }
+
+ if (value.isNull()) {
+ setRawHeaderInternal(name, QByteArray());
+ cookedHeaders.remove(header);
+ } else {
+ QByteArray rawValue = headerValue(header, value);
+ if (rawValue.isEmpty()) {
+ qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
+ value.typeName(), name.constData());
+ return;
+ }
+
+ setRawHeaderInternal(name, rawValue);
+ cookedHeaders.insert(header, value);
+ }
+}
+
+void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
+{
+ RawHeadersList::Iterator it = rawHeaders.begin();
+ while (it != rawHeaders.end()) {
+ if (qstricmp(it->first.constData(), key.constData()) == 0)
+ it = rawHeaders.erase(it);
+ else
+ ++it;
+ }
+
+ if (value.isNull())
+ return; // only wanted to erase key
+
+ RawHeaderPair pair;
+ pair.first = key;
+ pair.second = value;
+ rawHeaders.append(pair);
+}
+
+void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value)
+{
+ // is it a known header?
+ QNetworkRequest::KnownHeaders parsedKey = parseHeaderName(key);
+ if (parsedKey != QNetworkRequest::KnownHeaders(-1)) {
+ if (value.isNull()) {
+ cookedHeaders.remove(parsedKey);
+ } else if (parsedKey == QNetworkRequest::ContentLengthHeader
+ && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
+ // Only set the cooked header "Content-Length" once.
+ // See bug QTBUG-15311
+ } else {
+ cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value));
+ }
+
+ }
+}
+
+// Fast month string to int conversion. This code
+// assumes that the Month name is correct and that
+// the string is at least three chars long.
+static int name_to_month(const char* month_str)
+{
+ switch (month_str[0]) {
+ case 'J':
+ switch (month_str[1]) {
+ case 'a':
+ return 1;
+ break;
+ case 'u':
+ switch (month_str[2] ) {
+ case 'n':
+ return 6;
+ break;
+ case 'l':
+ return 7;
+ break;
+ }
+ }
+ break;
+ case 'F':
+ return 2;
+ break;
+ case 'M':
+ switch (month_str[2] ) {
+ case 'r':
+ return 3;
+ break;
+ case 'y':
+ return 5;
+ break;
+ }
+ break;
+ case 'A':
+ switch (month_str[1]) {
+ case 'p':
+ return 4;
+ break;
+ case 'u':
+ return 8;
+ break;
+ }
+ break;
+ case 'O':
+ return 10;
+ break;
+ case 'S':
+ return 9;
+ break;
+ case 'N':
+ return 11;
+ break;
+ case 'D':
+ return 12;
+ break;
+ }
+
+ return 0;
+}
+
+QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
+{
+ // HTTP dates have three possible formats:
+ // RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
+ // RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
+ // ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
+ // We only handle them exactly. If they deviate, we bail out.
+
+ int pos = value.indexOf(',');
+ QDateTime dt;
+#ifndef QT_NO_DATESTRING
+ if (pos == -1) {
+ // no comma -> asctime(3) format
+ dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
+ } else {
+ // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the
+ // QtWebKit performance benchmarks to get an idea.
+ if (pos == 3) {
+ char month_name[4];
+ int day, year, hour, minute, second;
+ if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6)
+ dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second));
+ } else {
+ QLocale c = QLocale::c();
+ // eat the weekday, the comma and the space following it
+ QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
+ // must be RFC 850 date
+ dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
+ }
+ }
+#endif // QT_NO_DATESTRING
+
+ if (dt.isValid())
+ dt.setTimeSpec(Qt::UTC);
+ return dt;
+}
+
+QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
+{
+ return QLocale::c().toString(dt, QLatin1String("ddd, dd MMM yyyy hh:mm:ss 'GMT'"))
+ .toLatin1();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
new file mode 100644
index 0000000000..d3bbba770c
--- /dev/null
+++ b/src/network/access/qnetworkrequest.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREQUEST_H
+#define QNETWORKREQUEST_H
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
+#include <QtCore/QVariant>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QSslConfiguration;
+
+class QNetworkRequestPrivate;
+class Q_NETWORK_EXPORT QNetworkRequest
+{
+public:
+ enum KnownHeaders {
+ ContentTypeHeader,
+ ContentLengthHeader,
+ LocationHeader,
+ LastModifiedHeader,
+ CookieHeader,
+ SetCookieHeader,
+ ContentDispositionHeader // added for QMultipartMessage
+ };
+ enum Attribute {
+ HttpStatusCodeAttribute,
+ HttpReasonPhraseAttribute,
+ RedirectionTargetAttribute,
+ ConnectionEncryptedAttribute,
+ CacheLoadControlAttribute,
+ CacheSaveControlAttribute,
+ SourceIsFromCacheAttribute,
+ DoNotBufferUploadDataAttribute,
+ HttpPipeliningAllowedAttribute,
+ HttpPipeliningWasUsedAttribute,
+ CustomVerbAttribute,
+ CookieLoadControlAttribute,
+ AuthenticationReuseAttribute,
+ CookieSaveControlAttribute,
+ MaximumDownloadBufferSizeAttribute, // internal
+ DownloadBufferAttribute, // internal
+ SynchronousRequestAttribute, // internal
+
+ User = 1000,
+ UserMax = 32767
+ };
+ enum CacheLoadControl {
+ AlwaysNetwork,
+ PreferNetwork,
+ PreferCache,
+ AlwaysCache
+ };
+ enum LoadControl {
+ Automatic = 0,
+ Manual
+ };
+
+ enum Priority {
+ HighPriority = 1,
+ NormalPriority = 3,
+ LowPriority = 5
+ };
+
+ explicit QNetworkRequest(const QUrl &url = QUrl());
+ QNetworkRequest(const QNetworkRequest &other);
+ ~QNetworkRequest();
+ QNetworkRequest &operator=(const QNetworkRequest &other);
+
+ bool operator==(const QNetworkRequest &other) const;
+ inline bool operator!=(const QNetworkRequest &other) const
+ { return !operator==(other); }
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ // "cooked" headers
+ QVariant header(KnownHeaders header) const;
+ void setHeader(KnownHeaders header, const QVariant &value);
+
+ // raw headers:
+ bool hasRawHeader(const QByteArray &headerName) const;
+ QList<QByteArray> rawHeaderList() const;
+ QByteArray rawHeader(const QByteArray &headerName) const;
+ void setRawHeader(const QByteArray &headerName, const QByteArray &value);
+
+ // attributes
+ QVariant attribute(Attribute code, const QVariant &defaultValue = QVariant()) const;
+ void setAttribute(Attribute code, const QVariant &value);
+
+#ifndef QT_NO_OPENSSL
+ QSslConfiguration sslConfiguration() const;
+ void setSslConfiguration(const QSslConfiguration &configuration);
+#endif
+
+ void setOriginatingObject(QObject *object);
+ QObject *originatingObject() const;
+
+ Priority priority() const;
+ void setPriority(Priority priority);
+
+private:
+ QSharedDataPointer<QNetworkRequestPrivate> d;
+ friend class QNetworkRequestPrivate;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QNetworkRequest)
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
new file mode 100644
index 0000000000..ea8c56f947
--- /dev/null
+++ b/src/network/access/qnetworkrequest_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREQUEST_P_H
+#define QNETWORKREQUEST_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 "qnetworkrequest.h"
+#include "QtCore/qbytearray.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qshareddata.h"
+#include "QtCore/qsharedpointer.h"
+
+QT_BEGIN_NAMESPACE
+
+// this is the common part between QNetworkRequestPrivate, QNetworkReplyPrivate and QHttpPartPrivate
+class QNetworkHeadersPrivate
+{
+public:
+ typedef QPair<QByteArray, QByteArray> RawHeaderPair;
+ typedef QList<RawHeaderPair> RawHeadersList;
+ typedef QHash<QNetworkRequest::KnownHeaders, QVariant> CookedHeadersMap;
+ typedef QHash<QNetworkRequest::Attribute, QVariant> AttributesMap;
+
+ RawHeadersList rawHeaders;
+ CookedHeadersMap cookedHeaders;
+ AttributesMap attributes;
+ 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);
+ void setCookedHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
+
+ static QDateTime fromHttpDate(const QByteArray &value);
+ static QByteArray toHttpDate(const QDateTime &dt);
+
+private:
+ void setRawHeaderInternal(const QByteArray &key, const QByteArray &value);
+ void parseAndSetHeader(const QByteArray &key, const QByteArray &value);
+};
+
+Q_DECLARE_TYPEINFO(QNetworkHeadersPrivate::RawHeaderPair, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+
+#endif
diff --git a/src/network/bearer/bearer.pri b/src/network/bearer/bearer.pri
new file mode 100644
index 0000000000..d58d5ec168
--- /dev/null
+++ b/src/network/bearer/bearer.pri
@@ -0,0 +1,19 @@
+# Qt network bearer management module
+
+HEADERS += bearer/qnetworkconfiguration.h \
+ bearer/qnetworksession.h \
+ bearer/qnetworkconfigmanager.h \
+ bearer/qnetworkconfigmanager_p.h \
+ bearer/qnetworkconfiguration_p.h \
+ bearer/qnetworksession_p.h \
+ bearer/qbearerengine_p.h \
+ bearer/qbearerplugin_p.h \
+ bearer/qsharednetworksession_p.h
+
+SOURCES += bearer/qnetworksession.cpp \
+ bearer/qnetworkconfigmanager.cpp \
+ bearer/qnetworkconfiguration.cpp \
+ bearer/qnetworkconfigmanager_p.cpp \
+ bearer/qbearerengine.cpp \
+ bearer/qbearerplugin.cpp \
+ bearer/qsharednetworksession.cpp
diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp
new file mode 100644
index 0000000000..7447051018
--- /dev/null
+++ b/src/network/bearer/qbearerengine.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbearerengine_p.h"
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+QBearerEngine::QBearerEngine(QObject *parent)
+ : QObject(parent), mutex(QMutex::Recursive)
+{
+}
+
+QBearerEngine::~QBearerEngine()
+{
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it;
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end;
+
+ for (it = snapConfigurations.begin(), end = snapConfigurations.end(); it != end; ++it) {
+ it.value()->isValid = false;
+ it.value()->id.clear();
+ }
+ snapConfigurations.clear();
+
+ for (it = accessPointConfigurations.begin(), end = accessPointConfigurations.end();
+ it != end; ++it) {
+ it.value()->isValid = false;
+ it.value()->id.clear();
+ }
+ accessPointConfigurations.clear();
+
+ for (it = userChoiceConfigurations.begin(), end = userChoiceConfigurations.end();
+ it != end; ++it) {
+ it.value()->isValid = false;
+ it.value()->id.clear();
+ }
+ userChoiceConfigurations.clear();
+}
+
+bool QBearerEngine::requiresPolling() const
+{
+ return false;
+}
+
+/*
+ Returns true if configurations are in use; otherwise returns false.
+
+ If configurations are in use and requiresPolling() returns true, polling will be enabled for
+ this engine.
+*/
+bool QBearerEngine::configurationsInUse() const
+{
+ QHash<QString, QNetworkConfigurationPrivatePointer>::ConstIterator it;
+ QHash<QString, QNetworkConfigurationPrivatePointer>::ConstIterator end;
+
+ QMutexLocker locker(&mutex);
+
+ for (it = accessPointConfigurations.constBegin(),
+ end = accessPointConfigurations.constEnd(); it != end; ++it) {
+ if (it.value()->ref > 1)
+ return true;
+ }
+
+ for (it = snapConfigurations.constBegin(),
+ end = snapConfigurations.constEnd(); it != end; ++it) {
+ if (it.value()->ref > 1)
+ return true;
+ }
+
+ for (it = userChoiceConfigurations.constBegin(),
+ end = userChoiceConfigurations.constEnd(); it != end; ++it) {
+ if (it.value()->ref > 1)
+ return true;
+ }
+
+ return false;
+}
+
+#include "moc_qbearerengine_p.cpp"
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+QT_END_NAMESPACE
diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h
new file mode 100644
index 0000000000..e246355142
--- /dev/null
+++ b/src/network/bearer/qbearerengine_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBEARERENGINE_P_H
+#define QBEARERENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworkconfiguration_p.h"
+#include "qnetworksession.h"
+#include "qnetworkconfigmanager.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qmutex.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkConfiguration;
+
+class Q_NETWORK_EXPORT QBearerEngine : public QObject
+{
+ Q_OBJECT
+
+ friend class QNetworkConfigurationManagerPrivate;
+
+public:
+ explicit QBearerEngine(QObject *parent = 0);
+ virtual ~QBearerEngine();
+
+ virtual bool hasIdentifier(const QString &id) = 0;
+
+ virtual QNetworkConfigurationManager::Capabilities capabilities() const = 0;
+
+ virtual QNetworkSessionPrivate *createSessionBackend() = 0;
+
+ virtual QNetworkConfigurationPrivatePointer defaultConfiguration() = 0;
+
+ virtual bool requiresPolling() const;
+ bool configurationsInUse() const;
+
+Q_SIGNALS:
+ void configurationAdded(QNetworkConfigurationPrivatePointer config);
+ void configurationRemoved(QNetworkConfigurationPrivatePointer config);
+ void configurationChanged(QNetworkConfigurationPrivatePointer config);
+ void updateCompleted();
+
+protected:
+ //this table contains an up to date list of all configs at any time.
+ //it must be updated if configurations change, are added/removed or
+ //the members of ServiceNetworks change
+ QHash<QString, QNetworkConfigurationPrivatePointer> accessPointConfigurations;
+ QHash<QString, QNetworkConfigurationPrivatePointer> snapConfigurations;
+ QHash<QString, QNetworkConfigurationPrivatePointer> userChoiceConfigurations;
+
+ mutable QMutex mutex;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif // QBEARERENGINE_P_H
diff --git a/src/network/bearer/qbearerplugin.cpp b/src/network/bearer/qbearerplugin.cpp
new file mode 100644
index 0000000000..c198d674ac
--- /dev/null
+++ b/src/network/bearer/qbearerplugin.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbearerplugin_p.h"
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+QBearerEnginePlugin::QBearerEnginePlugin(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QBearerEnginePlugin::~QBearerEnginePlugin()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h
new file mode 100644
index 0000000000..a800f90fb5
--- /dev/null
+++ b/src/network/bearer/qbearerplugin_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBEARERPLUGIN_P_H
+#define QBEARERPLUGIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qbearerengine_p.h"
+
+#include <QtCore/qplugin.h>
+#include <QtCore/qfactoryinterface.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+struct Q_NETWORK_EXPORT QBearerEngineFactoryInterface : public QFactoryInterface
+{
+ virtual QBearerEngine *create(const QString &key) const = 0;
+};
+
+#define QBearerEngineFactoryInterface_iid "com.trolltech.Qt.QBearerEngineFactoryInterface"
+Q_DECLARE_INTERFACE(QBearerEngineFactoryInterface, QBearerEngineFactoryInterface_iid)
+
+class Q_NETWORK_EXPORT QBearerEnginePlugin : public QObject, public QBearerEngineFactoryInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(QBearerEngineFactoryInterface:QFactoryInterface)
+
+public:
+ explicit QBearerEnginePlugin(QObject *parent = 0);
+ virtual ~QBearerEnginePlugin();
+
+ virtual QStringList keys() const = 0;
+ virtual QBearerEngine *create(const QString &key) const = 0;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif // QBEARERPLUGIN_P_H
diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp
new file mode 100644
index 0000000000..10fe74ce10
--- /dev/null
+++ b/src/network/bearer/qnetworkconfigmanager.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkconfigmanager.h"
+
+#include "qnetworkconfigmanager_p.h"
+#include "qbearerengine_p.h"
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qcoreapplication.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+#define Q_GLOBAL_STATIC_QAPP_DESTRUCTION(TYPE, NAME) \
+ static QGlobalStatic<TYPE > this_##NAME \
+ = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \
+ static void NAME##_cleanup() \
+ { \
+ delete this_##NAME.pointer; \
+ this_##NAME.pointer = 0; \
+ } \
+ static TYPE *NAME() \
+ { \
+ if (!this_##NAME.pointer) { \
+ TYPE *x = new TYPE; \
+ if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \
+ delete x; \
+ else { \
+ qAddPostRoutine(NAME##_cleanup); \
+ this_##NAME.pointer->updateConfigurations(); \
+ } \
+ } \
+ return this_##NAME.pointer; \
+ }
+
+Q_GLOBAL_STATIC_QAPP_DESTRUCTION(QNetworkConfigurationManagerPrivate, connManager);
+
+QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
+{
+ return connManager();
+}
+
+/*!
+ \class QNetworkConfigurationManager
+
+ \brief The QNetworkConfigurationManager class manages the network configurations provided
+ by the system.
+
+ \since 4.7
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ QNetworkConfigurationManager provides access to the network configurations known to the system and
+ enables applications to detect the system capabilities (with regards to network sessions) at runtime.
+
+ A QNetworkConfiguration abstracts a set of configuration options describing how a
+ network interface has to be configured to connect to a particular target network.
+ QNetworkConfigurationManager maintains and updates the global list of
+ QNetworkConfigurations. Applications can access and filter this list via
+ allConfigurations(). If a new configuration is added or an existing one is removed or changed
+ the configurationAdded(), configurationRemoved() and configurationChanged() signals are emitted
+ respectively.
+
+ The defaultConfiguration() can be used when intending to immediately create a new
+ network session without caring about the particular configuration. It returns
+ a \l QNetworkConfiguration::Discovered configuration. If there are not any
+ discovered ones an invalid configuration is returned.
+
+ Some configuration updates may require some time to perform updates. A WLAN scan is
+ such an example. Unless the platform performs internal updates it may be required to
+ manually trigger configuration updates via QNetworkConfigurationManager::updateConfigurations().
+ The completion of the update process is indicted by emitting the updateCompleted()
+ signal. The update process ensures that every existing QNetworkConfiguration instance
+ is updated. There is no need to ask for a renewed configuration list via allConfigurations().
+
+ \sa QNetworkConfiguration
+*/
+
+/*!
+ \fn void QNetworkConfigurationManager::configurationAdded(const QNetworkConfiguration &config)
+
+ This signal is emitted whenever a new network configuration is added to the system. The new
+ configuration is specified by \a config.
+*/
+
+/*!
+ \fn void QNetworkConfigurationManager::configurationRemoved(const QNetworkConfiguration &config)
+
+ This signal is emitted when a configuration is about to be removed from the system. The removed
+ \a configuration is invalid but retains name and identifier.
+*/
+
+/*!
+ \fn void QNetworkConfigurationManager::updateCompleted()
+
+ This signal is emitted when the configuration update has been completed. Such an update can
+ be initiated via \l updateConfigurations().
+*/
+
+/*! \fn void QNetworkConfigurationManager::configurationChanged(const QNetworkConfiguration &config)
+
+ This signal is emitted when the \l {QNetworkConfiguration::state()}{state} of \a config changes.
+*/
+
+/*!
+ \fn void QNetworkConfigurationManager::onlineStateChanged(bool isOnline)
+
+ This signal is emitted when the device changes from online to offline mode or vice versa.
+ \a isOnline represents the new state of the device.
+
+ The state is considered to be online for as long as
+ \l{allConfigurations()}{allConfigurations}(QNetworkConfiguration::Active) returns a list with
+ at least one entry.
+*/
+
+/*!
+ \enum QNetworkConfigurationManager::Capability
+
+ Specifies the system capabilities of the bearer API. The possible values are:
+
+ \value CanStartAndStopInterfaces Network sessions and their underlying access points can be
+ started and stopped. If this flag is not set QNetworkSession
+ can only monitor but not influence the state of access points.
+ On some platforms this feature may require elevated user
+ permissions. This option is platform specific and may not
+ always be available.
+ \value DirectConnectionRouting Network sessions and their sockets can be bound to a
+ particular network interface. Any packet that passes through
+ the socket goes to the specified network interface and thus
+ disregards standard routing table entries. This may be useful
+ when two interfaces can reach overlapping IP ranges or an
+ application has specific needs in regards to target networks.
+ This option is platform specific and may not always be
+ available.
+ \value SystemSessionSupport If this flag is set the underlying platform ensures that a
+ network interface is not shut down until the last network
+ session has been \l{QNetworkSession::close()}{closed()}. This
+ works across multiple processes. If the platform session
+ support is missing this API can only ensure the above behavior
+ for network sessions within the same process.
+ In general mobile platforms (such as Symbian/S60) have such
+ support whereas most desktop platform lack this capability.
+ \value ApplicationLevelRoaming The system gives applications control over the systems roaming
+ behavior. Applications can initiate roaming (in case the
+ current link is not suitable) and are consulted if the system
+ has identified a more suitable access point.
+ \value ForcedRoaming The system disconnects an existing access point and reconnects
+ via a more suitable one. The application does not have any
+ control over this process and has to reconnect its active
+ sockets.
+ \value DataStatistics If this flag is set QNetworkSession can provide statistics
+ about transmitted and received data.
+ \value NetworkSessionRequired If this flag is set the platform requires that a network
+ session is created before network operations can be performed.
+*/
+
+/*!
+ Constructs a QNetworkConfigurationManager with the given \a parent.
+
+ 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)
+ : QObject(parent)
+{
+ QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
+
+ connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)),
+ this, SIGNAL(configurationAdded(QNetworkConfiguration)));
+ connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)),
+ this, SIGNAL(configurationRemoved(QNetworkConfiguration)));
+ 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();
+}
+
+/*!
+ Frees the resources associated with the QNetworkConfigurationManager object.
+*/
+QNetworkConfigurationManager::~QNetworkConfigurationManager()
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ priv->disablePolling();
+}
+
+
+/*!
+ Returns the default configuration to be used. This function always returns a discovered
+ configuration; otherwise an invalid configuration.
+
+ In some cases it may be required to call updateConfigurations() and wait for the
+ updateCompleted() signal before calling this function.
+
+ \sa allConfigurations()
+*/
+QNetworkConfiguration QNetworkConfigurationManager::defaultConfiguration() const
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ return priv->defaultConfiguration();
+
+ return QNetworkConfiguration();
+}
+
+/*!
+ Returns the list of configurations which comply with the given \a filter.
+
+ By default this function returns all (defined and undefined) configurations.
+
+ A wireless network with a particular SSID may only be accessible in a
+ certain area despite the fact that the system has a valid configuration
+ for it. Therefore the filter flag may be used to limit the list to
+ discovered and possibly connected configurations only.
+
+ If \a filter is set to zero this function returns all possible configurations.
+
+ Note that this function returns the states for all configurations as they are known at
+ the time of this function call. If for instance a configuration of type WLAN is defined
+ the system may have to perform a WLAN scan in order to determine whether it is
+ actually available. To obtain the most accurate state updateConfigurations() should
+ be used to update each configuration's state. Note that such an update may require
+ some time. It's completion is signalled by updateCompleted(). In the absence of a
+ configuration update this function returns the best estimate at the time of the call.
+ Therefore, if WLAN configurations are of interest, it is recommended that
+ updateConfigurations() is called once after QNetworkConfigurationManager
+ instantiation (WLAN scans are too time consuming to perform in constructor).
+ After this the data is kept automatically up-to-date as the system reports
+ any changes.
+*/
+QList<QNetworkConfiguration> QNetworkConfigurationManager::allConfigurations(QNetworkConfiguration::StateFlags filter) const
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ return priv->allConfigurations(filter);
+
+ return QList<QNetworkConfiguration>();
+}
+
+/*!
+ Returns the QNetworkConfiguration for \a identifier; otherwise returns an
+ invalid QNetworkConfiguration.
+
+ \sa QNetworkConfiguration::identifier()
+*/
+QNetworkConfiguration QNetworkConfigurationManager::configurationFromIdentifier(const QString &identifier) const
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ return priv->configurationFromIdentifier(identifier);
+
+ return QNetworkConfiguration();
+}
+
+/*!
+ Returns true if the system is considered to be connected to another device via an active
+ network interface; otherwise returns false.
+
+ This is equivalent to the following code snippet:
+
+ \snippet doc/src/snippets/code/src_network_bearer_qnetworkconfigmanager.cpp 0
+
+ \sa onlineStateChanged()
+*/
+bool QNetworkConfigurationManager::isOnline() const
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ return priv->isOnline();
+
+ return false;
+}
+
+/*!
+ Returns the capabilities supported by the current platform.
+*/
+QNetworkConfigurationManager::Capabilities QNetworkConfigurationManager::capabilities() const
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ return priv->capabilities();
+
+ return QNetworkConfigurationManager::Capabilities(0);
+}
+
+/*!
+ Initiates an update of all configurations. This may be used to initiate WLAN scans or other
+ time consuming updates which may be required to obtain the correct state for configurations.
+
+ This call is asynchronous. On completion of this update the updateCompleted() signal is
+ emitted. If new configurations are discovered or old ones were removed or changed the update
+ process may trigger the emission of one or multiple configurationAdded(),
+ configurationRemoved() and configurationChanged() signals.
+
+ If a configuration state changes as a result of this update all existing QNetworkConfiguration
+ instances are updated automatically.
+
+ \sa allConfigurations()
+*/
+void QNetworkConfigurationManager::updateConfigurations()
+{
+ QNetworkConfigurationManagerPrivate *priv = connManager();
+ if (priv)
+ priv->performAsyncConfigurationUpdate();
+}
+
+#include "moc_qnetworkconfigmanager.cpp"
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qnetworkconfigmanager.h b/src/network/bearer/qnetworkconfigmanager.h
new file mode 100644
index 0000000000..565156cbc5
--- /dev/null
+++ b/src/network/bearer/qnetworkconfigmanager.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCONFIGURATIONMANAGER_H
+#define QNETWORKCONFIGURATIONMANAGER_H
+
+#ifdef QT_MOBILITY_BEARER
+# include "qmobilityglobal.h"
+#endif
+
+#include <QtCore/qobject.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_HEADER
+
+#ifndef QT_MOBILITY_BEARER
+QT_BEGIN_NAMESPACE
+#define QNetworkConfigurationManagerExport Q_NETWORK_EXPORT
+QT_MODULE(Network)
+#else
+QTM_BEGIN_NAMESPACE
+#define QNetworkConfigurationManagerExport Q_BEARER_EXPORT
+#endif
+
+class QNetworkConfigurationManagerPrivate;
+class QNetworkConfigurationManagerExport QNetworkConfigurationManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Capability {
+ CanStartAndStopInterfaces = 0x00000001,
+ DirectConnectionRouting = 0x00000002,
+ SystemSessionSupport = 0x00000004,
+ ApplicationLevelRoaming = 0x00000008,
+ ForcedRoaming = 0x00000010,
+ DataStatistics = 0x00000020,
+ NetworkSessionRequired = 0x00000040
+ };
+
+ Q_DECLARE_FLAGS(Capabilities, Capability)
+
+ explicit QNetworkConfigurationManager(QObject *parent = 0);
+ virtual ~QNetworkConfigurationManager();
+
+ QNetworkConfigurationManager::Capabilities capabilities() const;
+
+ QNetworkConfiguration defaultConfiguration() const;
+ QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags flags = 0) const;
+ QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const;
+
+ 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 onlineStateChanged(bool isOnline);
+ void updateCompleted();
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkConfigurationManager::Capabilities)
+
+#ifndef QT_MOBILITY_BEARER
+QT_END_NAMESPACE
+#else
+QTM_END_NAMESPACE
+#endif
+
+QT_END_HEADER
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif // QNETWORKCONFIGURATIONMANAGER_H
diff --git a/src/network/bearer/qnetworkconfigmanager_p.cpp b/src/network/bearer/qnetworkconfigmanager_p.cpp
new file mode 100644
index 0000000000..c108ad34cf
--- /dev/null
+++ b/src/network/bearer/qnetworkconfigmanager_p.cpp
@@ -0,0 +1,484 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkconfigmanager_p.h"
+#include "qbearerplugin_p.h"
+
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qthread.h>
+#include <QtCore/private/qcoreapplication_p.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LIBRARY
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QBearerEngineFactoryInterface_iid, QLatin1String("/bearer")))
+#endif
+
+QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate()
+ : QObject(), mutex(QMutex::Recursive), forcedPolling(0), firstUpdate(true)
+{
+ qRegisterMetaType<QNetworkConfiguration>("QNetworkConfiguration");
+ qRegisterMetaType<QNetworkConfigurationPrivatePointer>("QNetworkConfigurationPrivatePointer");
+}
+
+QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate()
+{
+ QMutexLocker locker(&mutex);
+
+ qDeleteAll(sessionEngines);
+}
+
+QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const
+{
+ QMutexLocker locker(&mutex);
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ QNetworkConfigurationPrivatePointer ptr = engine->defaultConfiguration();
+ if (ptr) {
+ QNetworkConfiguration config;
+ config.d = ptr;
+ return config;
+ }
+ }
+
+ // Engines don't have a default configuration.
+
+ // Return first active snap
+ QNetworkConfigurationPrivatePointer defaultConfiguration;
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it;
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end;
+
+ QMutexLocker locker(&engine->mutex);
+
+ for (it = engine->snapConfigurations.begin(),
+ end = engine->snapConfigurations.end(); it != end; ++it) {
+ QNetworkConfigurationPrivatePointer ptr = it.value();
+
+ QMutexLocker configLocker(&ptr->mutex);
+
+ if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
+ QNetworkConfiguration config;
+ config.d = ptr;
+ return config;
+ } else if (!defaultConfiguration) {
+ if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered)
+ defaultConfiguration = ptr;
+ }
+ }
+ }
+
+ // No Active SNAPs return first Discovered SNAP.
+ if (defaultConfiguration) {
+ QNetworkConfiguration config;
+ config.d = defaultConfiguration;
+ return config;
+ }
+
+ /*
+ No Active or Discovered SNAPs, find the perferred access point.
+ The following priority order is used:
+
+ 1. Active Ethernet
+ 2. Active WLAN
+ 3. Active Other
+ 4. Discovered Ethernet
+ 5. Discovered WLAN
+ 6. Discovered Other
+ */
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it;
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end;
+
+ QMutexLocker locker(&engine->mutex);
+
+ for (it = engine->accessPointConfigurations.begin(),
+ end = engine->accessPointConfigurations.end(); it != end; ++it) {
+ QNetworkConfigurationPrivatePointer ptr = it.value();
+
+ QMutexLocker configLocker(&ptr->mutex);
+ QNetworkConfiguration::BearerType bearerType = ptr->bearerType;
+
+ if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) {
+ if (!defaultConfiguration) {
+ defaultConfiguration = ptr;
+ } else {
+ QMutexLocker defaultConfigLocker(&defaultConfiguration->mutex);
+
+ if (defaultConfiguration->state == ptr->state) {
+ switch (defaultConfiguration->bearerType) {
+ case QNetworkConfiguration::BearerEthernet:
+ // do nothing
+ break;
+ case QNetworkConfiguration::BearerWLAN:
+ // Ethernet beats WLAN
+ defaultConfiguration = ptr;
+ break;
+ default:
+ // Ethernet and WLAN beats other
+ if (bearerType == QNetworkConfiguration::BearerEthernet ||
+ bearerType == QNetworkConfiguration::BearerWLAN) {
+ defaultConfiguration = ptr;
+ }
+ }
+ } else {
+ // active beats discovered
+ if ((defaultConfiguration->state & QNetworkConfiguration::Active) !=
+ QNetworkConfiguration::Active) {
+ defaultConfiguration = ptr;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // No Active InternetAccessPoint return first Discovered InternetAccessPoint.
+ if (defaultConfiguration) {
+ QNetworkConfiguration config;
+ config.d = defaultConfiguration;
+ return config;
+ }
+
+ return QNetworkConfiguration();
+}
+
+QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) const
+{
+ QList<QNetworkConfiguration> result;
+
+ QMutexLocker locker(&mutex);
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator it;
+ QHash<QString, QNetworkConfigurationPrivatePointer>::Iterator end;
+
+ QMutexLocker locker(&engine->mutex);
+
+ //find all InternetAccessPoints
+ for (it = engine->accessPointConfigurations.begin(),
+ end = engine->accessPointConfigurations.end(); it != end; ++it) {
+ QNetworkConfigurationPrivatePointer ptr = it.value();
+
+ QMutexLocker configLocker(&ptr->mutex);
+
+ if ((ptr->state & filter) == filter) {
+ QNetworkConfiguration pt;
+ pt.d = ptr;
+ result << pt;
+ }
+ }
+
+ //find all service networks
+ for (it = engine->snapConfigurations.begin(),
+ end = engine->snapConfigurations.end(); it != end; ++it) {
+ QNetworkConfigurationPrivatePointer ptr = it.value();
+
+ QMutexLocker configLocker(&ptr->mutex);
+
+ if ((ptr->state & filter) == filter) {
+ QNetworkConfiguration pt;
+ pt.d = ptr;
+ result << pt;
+ }
+ }
+ }
+
+ return result;
+}
+
+QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) const
+{
+ QNetworkConfiguration item;
+
+ QMutexLocker locker(&mutex);
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ QMutexLocker locker(&engine->mutex);
+
+ if (engine->accessPointConfigurations.contains(identifier))
+ item.d = engine->accessPointConfigurations[identifier];
+ else if (engine->snapConfigurations.contains(identifier))
+ item.d = engine->snapConfigurations[identifier];
+ else if (engine->userChoiceConfigurations.contains(identifier))
+ item.d = engine->userChoiceConfigurations[identifier];
+ else
+ continue;
+
+ return item;
+ }
+
+ return item;
+}
+
+bool QNetworkConfigurationManagerPrivate::isOnline() const
+{
+ QMutexLocker locker(&mutex);
+
+ return !onlineConfigurations.isEmpty();
+}
+
+QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() const
+{
+ QMutexLocker locker(&mutex);
+
+ QNetworkConfigurationManager::Capabilities capFlags;
+
+ foreach (QBearerEngine *engine, sessionEngines)
+ capFlags |= engine->capabilities();
+
+ return capFlags;
+}
+
+void QNetworkConfigurationManagerPrivate::configurationAdded(QNetworkConfigurationPrivatePointer ptr)
+{
+ QMutexLocker locker(&mutex);
+
+ if (!firstUpdate) {
+ QNetworkConfiguration item;
+ item.d = ptr;
+ emit configurationAdded(item);
+ }
+
+ ptr->mutex.lock();
+ if (ptr->state == QNetworkConfiguration::Active) {
+ ptr->mutex.unlock();
+ onlineConfigurations.insert(ptr->id);
+ if (!firstUpdate && onlineConfigurations.count() == 1)
+ emit onlineStateChanged(true);
+ } else {
+ ptr->mutex.unlock();
+ }
+}
+
+void QNetworkConfigurationManagerPrivate::configurationRemoved(QNetworkConfigurationPrivatePointer ptr)
+{
+ QMutexLocker locker(&mutex);
+
+ ptr->mutex.lock();
+ ptr->isValid = false;
+ ptr->mutex.unlock();
+
+ if (!firstUpdate) {
+ QNetworkConfiguration item;
+ item.d = ptr;
+ emit configurationRemoved(item);
+ }
+
+ onlineConfigurations.remove(ptr->id);
+ if (!firstUpdate && onlineConfigurations.isEmpty())
+ emit onlineStateChanged(false);
+}
+
+void QNetworkConfigurationManagerPrivate::configurationChanged(QNetworkConfigurationPrivatePointer ptr)
+{
+ QMutexLocker locker(&mutex);
+
+ if (!firstUpdate) {
+ QNetworkConfiguration item;
+ item.d = ptr;
+ emit configurationChanged(item);
+ }
+
+ bool previous = !onlineConfigurations.isEmpty();
+
+ ptr->mutex.lock();
+ if (ptr->state == QNetworkConfiguration::Active)
+ onlineConfigurations.insert(ptr->id);
+ else
+ onlineConfigurations.remove(ptr->id);
+ ptr->mutex.unlock();
+
+ bool online = !onlineConfigurations.isEmpty();
+
+ if (!firstUpdate && online != previous)
+ emit onlineStateChanged(online);
+}
+
+void QNetworkConfigurationManagerPrivate::updateConfigurations()
+{
+ QMutexLocker locker(&mutex);
+
+ if (firstUpdate) {
+ if (qobject_cast<QBearerEngine *>(sender()))
+ return;
+
+ if (thread() != QCoreApplicationPrivate::mainThread()) {
+ if (thread() != QThread::currentThread())
+ return;
+
+ moveToThread(QCoreApplicationPrivate::mainThread());
+ }
+
+ updating = false;
+
+#ifndef QT_NO_LIBRARY
+ QBearerEngine *generic = 0;
+
+ QFactoryLoader *l = loader();
+ foreach (const QString &key, l->keys()) {
+ QBearerEnginePlugin *plugin = qobject_cast<QBearerEnginePlugin *>(l->instance(key));
+ if (plugin) {
+ QBearerEngine *engine = plugin->create(key);
+ if (!engine)
+ continue;
+
+ if (key == QLatin1String("generic"))
+ generic = engine;
+ else
+ sessionEngines.append(engine);
+
+ engine->moveToThread(QCoreApplicationPrivate::mainThread());
+
+ connect(engine, SIGNAL(updateCompleted()),
+ this, SLOT(updateConfigurations()));
+ connect(engine, SIGNAL(configurationAdded(QNetworkConfigurationPrivatePointer)),
+ this, SLOT(configurationAdded(QNetworkConfigurationPrivatePointer)));
+ connect(engine, SIGNAL(configurationRemoved(QNetworkConfigurationPrivatePointer)),
+ this, SLOT(configurationRemoved(QNetworkConfigurationPrivatePointer)));
+ connect(engine, SIGNAL(configurationChanged(QNetworkConfigurationPrivatePointer)),
+ this, SLOT(configurationChanged(QNetworkConfigurationPrivatePointer)));
+
+ QMetaObject::invokeMethod(engine, "initialize");
+ }
+ }
+
+ if (generic)
+ sessionEngines.append(generic);
+#endif // QT_NO_LIBRARY
+ }
+
+ QBearerEngine *engine = qobject_cast<QBearerEngine *>(sender());
+ if (engine && !updatingEngines.isEmpty())
+ updatingEngines.remove(engine);
+
+ if (updating && updatingEngines.isEmpty()) {
+ updating = false;
+ emit configurationUpdateComplete();
+ }
+
+ if (engine && !pollingEngines.isEmpty()) {
+ pollingEngines.remove(engine);
+ if (pollingEngines.isEmpty())
+ startPolling();
+ }
+
+ if (firstUpdate)
+ firstUpdate = false;
+}
+
+void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate()
+{
+ QMutexLocker locker(&mutex);
+
+ if (sessionEngines.isEmpty()) {
+ emit configurationUpdateComplete();
+ return;
+ }
+
+ updating = true;
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ updatingEngines.insert(engine);
+ QMetaObject::invokeMethod(engine, "requestUpdate");
+ }
+}
+
+QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const
+{
+ QMutexLocker locker(&mutex);
+
+ return sessionEngines;
+}
+
+void QNetworkConfigurationManagerPrivate::startPolling()
+{
+ QMutexLocker locker(&mutex);
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
+ QTimer::singleShot(10000, this, SLOT(pollEngines()));
+ break;
+ }
+ }
+}
+
+void QNetworkConfigurationManagerPrivate::pollEngines()
+{
+ QMutexLocker locker(&mutex);
+
+ foreach (QBearerEngine *engine, sessionEngines) {
+ if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
+ pollingEngines.insert(engine);
+ QMetaObject::invokeMethod(engine, "requestUpdate");
+ }
+ }
+}
+
+void QNetworkConfigurationManagerPrivate::enablePolling()
+{
+ QMutexLocker locker(&mutex);
+
+ ++forcedPolling;
+
+ if (forcedPolling == 1)
+ startPolling();
+}
+
+void QNetworkConfigurationManagerPrivate::disablePolling()
+{
+ QMutexLocker locker(&mutex);
+
+ --forcedPolling;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h
new file mode 100644
index 0000000000..81f38c5183
--- /dev/null
+++ b/src/network/bearer/qnetworkconfigmanager_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCONFIGURATIONMANAGERPRIVATE_H
+#define QNETWORKCONFIGURATIONMANAGERPRIVATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworkconfigmanager.h"
+#include "qnetworkconfiguration_p.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qset.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+class QBearerEngine;
+
+class Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ QNetworkConfigurationManagerPrivate();
+ virtual ~QNetworkConfigurationManagerPrivate();
+
+ QNetworkConfiguration defaultConfiguration() const;
+ QList<QNetworkConfiguration> allConfigurations(QNetworkConfiguration::StateFlags filter) const;
+ QNetworkConfiguration configurationFromIdentifier(const QString &identifier) const;
+
+ bool isOnline() const;
+
+ QNetworkConfigurationManager::Capabilities capabilities() const;
+
+ void performAsyncConfigurationUpdate();
+
+ QList<QBearerEngine *> engines() const;
+
+ void enablePolling();
+ void disablePolling();
+
+public Q_SLOTS:
+ void updateConfigurations();
+
+Q_SIGNALS:
+ void configurationAdded(const QNetworkConfiguration &config);
+ void configurationRemoved(const QNetworkConfiguration &config);
+ void configurationChanged(const QNetworkConfiguration &config);
+ void configurationUpdateComplete();
+ void onlineStateChanged(bool isOnline);
+
+private Q_SLOTS:
+ void configurationAdded(QNetworkConfigurationPrivatePointer ptr);
+ void configurationRemoved(QNetworkConfigurationPrivatePointer ptr);
+ void configurationChanged(QNetworkConfigurationPrivatePointer ptr);
+
+ void pollEngines();
+
+private:
+ void startPolling();
+
+private:
+ mutable QMutex mutex;
+
+ QList<QBearerEngine *> sessionEngines;
+
+ QSet<QString> onlineConfigurations;
+
+ QSet<QBearerEngine *> pollingEngines;
+ QSet<QBearerEngine *> updatingEngines;
+ int forcedPolling;
+ bool updating;
+
+ bool firstUpdate;
+};
+
+Q_NETWORK_EXPORT QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate();
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif // QNETWORKCONFIGURATIONMANAGERPRIVATE_H
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
new file mode 100644
index 0000000000..4cc3099b17
--- /dev/null
+++ b/src/network/bearer/qnetworkconfiguration.cpp
@@ -0,0 +1,509 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkconfiguration.h"
+#include "qnetworkconfiguration_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkConfiguration
+
+ \brief The QNetworkConfiguration class provides an abstraction of one or more access point configurations.
+
+ \since 4.7
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ QNetworkConfiguration encapsulates a single access point or service network.
+ In most cases a single access point configuration can be mapped to one network
+ interface. However a single network interface may not always map to only one
+ access point configuration. Multiple configurations for the same
+ network device may enable multiple access points. An example
+ device that could exhibit such a configuration might be a
+ Smartphone which allows the user to manage multiple WLAN
+ configurations while the device itself has only one WLAN network device.
+
+ The QNetworkConfiguration also supports the concept of service networks.
+ This concept allows the grouping of multiple access point configurations
+ into one entity. Such a group is called service network and can be
+ beneficial in cases whereby a network session to a
+ particular destination network is required (e.g. a company network).
+ When using a service network the user doesn't usually care which one of the
+ connectivity options is chosen (e.g. corporate WLAN or VPN via GPRS)
+ as long as he can reach the company's target server. Depending
+ on the current position and time some of the access points that make
+ up the service network may not even be available. Furthermore
+ automated access point roaming can be enabled which enables the device
+ to change the network interface configuration dynamically while maintaining
+ the applications connection to the target network. It allows adaption
+ to the changing environment and may enable optimization with regards to
+ cost, speed or other network parameters.
+
+ Special configurations of type UserChoice provide a placeholder configuration which is
+ resolved to an actual network configuration by the platform when a
+ \l {QNetworkSession}{session} is \l {QNetworkSession::open()}{opened}. Not all platforms
+ support the concept of a user choice configuration.
+
+ \section1 Configuration states
+
+ The list of available configurations can be obtained via
+ QNetworkConfigurationManager::allConfigurations(). A configuration can have
+ multiple states. The \l Defined configuration state indicates that the configuration
+ is stored on the device. However the configuration is not yet ready to be activated
+ as e.g. a WLAN may not be available at the current time.
+
+ The \l Discovered state implies that the configuration is \l Defined and
+ the outside conditions are such that the configuration can be used immediately
+ to open a new network session. An example of such an outside condition may be
+ that the Ethernet cable is actually connected to the device or that the WLAN
+ with the specified SSID is in range.
+
+ The \l Active state implies that the configuration is \l Discovered. A configuration
+ in this state is currently being used by an application. The underlying network
+ interface has a valid IP configuration and can transfer IP packets between the
+ device and the target network.
+
+ The \l Undefined state indicates that the system has knowledge of possible target
+ networks but cannot actually use that knowledge to connect to it. An example
+ for such a state could be an encrypted WLAN that has been discovered
+ but the user hasn't actually saved a configuration including the required password
+ which would allow the device to connect to it.
+
+ Depending on the type of configuration some states are transient in nature. A GPRS/UMTS
+ connection may almost always be \l Discovered if the GSM/UMTS network is available.
+ However if the GSM/UMTS network looses the connection the associated configuration may change its state
+ from \l Discovered to \l Defined as well. A similar use case might be triggered by
+ WLAN availability. QNetworkConfigurationManager::updateConfigurations() can be used to
+ manually trigger updates of states. Note that some platforms do not require such updates
+ as they implicitly change the state once it has been discovered. If the state of a
+ configuration changes all related QNetworkConfiguration instances change their state automatically.
+
+ \sa QNetworkSession, QNetworkConfigurationManager
+*/
+
+/*!
+ \enum QNetworkConfiguration::Type
+
+ This enum describes the type of configuration.
+
+ \value InternetAccessPoint The configuration specifies the details for a single access point.
+ Note that configurations of type InternetAccessPoint may be part
+ of other QNetworkConfigurations of type ServiceNetwork.
+ \value ServiceNetwork The configuration is based on a group of QNetworkConfigurations of
+ type InternetAccessPoint. All group members can reach the same
+ target network. This type of configuration is a mandatory
+ requirement for roaming enabled network sessions. On some
+ platforms this form of configuration may also be called Service
+ Network Access Point (SNAP).
+ \value UserChoice The configuration is a placeholder which will be resolved to an
+ actual configuration by the platform when a session is opened. Depending
+ on the platform the selection may generate a popup dialog asking the user
+ for his preferred choice.
+ \value Invalid The configuration is invalid.
+*/
+
+/*!
+ \enum QNetworkConfiguration::StateFlag
+
+ Specifies the configuration states.
+
+ \value Undefined This state is used for transient configurations such as newly discovered
+ WLANs for which the user has not actually created a configuration yet.
+ \value Defined Defined configurations are known to the system but are not immediately
+ usable (e.g. a configured WLAN is not within range or the Ethernet cable
+ is currently not plugged into the machine).
+ \value Discovered A discovered configuration can be immediately used to create a new
+ QNetworkSession. An example of a discovered configuration could be a WLAN
+ which is within in range. If the device moves out of range the discovered
+ flag is dropped. A second example is a GPRS configuration which generally
+ remains discovered for as long as the device has network coverage. A
+ configuration that has this state is also in state
+ QNetworkConfiguration::Defined. If the configuration is a service network
+ this flag is set if at least one of the underlying access points
+ configurations has the Discovered state.
+ \value Active The configuration is currently used by an open network session
+ (see \l QNetworkSession::isOpen()). However this does not mean that the
+ current process is the entity that created the open session. It merely
+ indicates that if a new QNetworkSession were to be constructed based on
+ this configuration \l QNetworkSession::state() would return
+ \l QNetworkSession::Connected. This state implies the
+ QNetworkConfiguration::Discovered state.
+*/
+
+/*!
+ \enum QNetworkConfiguration::Purpose
+
+ Specifies the purpose of the configuration.
+
+ \value UnknownPurpose The configuration doesn't specify any purpose. This is the default value.
+ \value PublicPurpose The configuration can be used for general purpose internet access.
+ \value PrivatePurpose The configuration is suitable to access a private network such as an office Intranet.
+ \value ServiceSpecificPurpose The configuration can be used for operator specific services (e.g.
+ receiving MMS messages or content streaming).
+*/
+
+/*!
+ \enum QNetworkConfiguration::BearerType
+
+ Specifies the type of bearer used by a configuration.
+
+ \value BearerUnknown The type of bearer is unknown or unspecified. The bearerTypeName()
+ function may return additional information.
+ \value BearerEthernet The configuration is for an Ethernet interfaces.
+ \value BearerWLAN The configuration is for a Wireless LAN interface.
+ \value Bearer2G The configuration is for a CSD, GPRS, HSCSD, EDGE or cdmaOne interface.
+ \value BearerCDMA2000 The configuration is for CDMA interface.
+ \value BearerWCDMA The configuration is for W-CDMA/UMTS interface.
+ \value BearerHSPA The configuration is for High Speed Packet Access (HSPA) interface.
+ \value BearerBluetooth The configuration is for a Bluetooth interface.
+ \value BearerWiMAX The configuration is for a WiMAX interface.
+*/
+
+/*!
+ Constructs an invalid configuration object.
+
+ \sa isValid()
+*/
+QNetworkConfiguration::QNetworkConfiguration()
+ : d(0)
+{
+}
+
+/*!
+ Creates a copy of the QNetworkConfiguration object contained in \a other.
+*/
+QNetworkConfiguration::QNetworkConfiguration(const QNetworkConfiguration &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Frees the resources associated with the QNetworkConfiguration object.
+*/
+QNetworkConfiguration::~QNetworkConfiguration()
+{
+}
+
+/*!
+ Copies the content of the QNetworkConfiguration object contained in \a other into this one.
+*/
+QNetworkConfiguration &QNetworkConfiguration::operator=(const QNetworkConfiguration &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true, if this configuration is the same as the \a other
+ configuration given; otherwise returns false.
+*/
+bool QNetworkConfiguration::operator==(const QNetworkConfiguration &other) const
+{
+ return (d == other.d);
+}
+
+/*!
+ \fn bool QNetworkConfiguration::operator!=(const QNetworkConfiguration &other) const
+
+ Returns true if this configuration is not the same as the \a other
+ configuration given; otherwise returns false.
+*/
+
+/*!
+ Returns the user visible name of this configuration.
+
+ The name may either be the name of the underlying access point or the
+ name for service network that this configuration represents.
+*/
+QString QNetworkConfiguration::name() const
+{
+ if (!d)
+ return QString();
+
+ QMutexLocker locker(&d->mutex);
+ return d->name;
+}
+
+/*!
+ Returns the unique and platform specific identifier for this network configuration;
+ otherwise an empty string.
+*/
+QString QNetworkConfiguration::identifier() const
+{
+ if (!d)
+ return QString();
+
+ QMutexLocker locker(&d->mutex);
+ return d->id;
+}
+
+/*!
+ Returns the type of the configuration.
+
+ A configuration can represent a single access point configuration or
+ a set of access point configurations. Such a set is called service network.
+ A configuration that is based on a service network can potentially support
+ roaming of network sessions.
+*/
+QNetworkConfiguration::Type QNetworkConfiguration::type() const
+{
+ if (!d)
+ return QNetworkConfiguration::Invalid;
+
+ QMutexLocker locker(&d->mutex);
+ return d->type;
+}
+
+/*!
+ Returns true if this QNetworkConfiguration object is valid.
+ A configuration may become invalid if the user deletes the configuration or
+ the configuration was default-constructed.
+
+ The addition and removal of configurations can be monitored via the
+ QNetworkConfigurationManager.
+
+ \sa QNetworkConfigurationManager
+*/
+bool QNetworkConfiguration::isValid() const
+{
+ if (!d)
+ return false;
+
+ QMutexLocker locker(&d->mutex);
+ return d->isValid;
+}
+
+/*!
+ Returns the current state of the configuration.
+*/
+QNetworkConfiguration::StateFlags QNetworkConfiguration::state() const
+{
+ if (!d)
+ return QNetworkConfiguration::Undefined;
+
+ QMutexLocker locker(&d->mutex);
+ return d->state;
+}
+
+/*!
+ Returns the purpose of this configuration.
+
+ The purpose field may be used to programmatically determine the
+ purpose of a configuration. Such information is usually part of the
+ access point or service network meta data.
+*/
+QNetworkConfiguration::Purpose QNetworkConfiguration::purpose() const
+{
+ if (!d)
+ return QNetworkConfiguration::UnknownPurpose;
+
+ QMutexLocker locker(&d->mutex);
+ return d->purpose;
+}
+
+/*!
+ Returns true if this configuration supports roaming; otherwise false.
+*/
+bool QNetworkConfiguration::isRoamingAvailable() const
+{
+ if (!d)
+ return false;
+
+ QMutexLocker locker(&d->mutex);
+ return d->roamingSupported;
+}
+
+/*!
+ Returns all sub configurations of this network configuration in priority order. The first sub
+ configuration in the list has the highest priority.
+
+ Only network configurations of type \l ServiceNetwork can have children. Otherwise this
+ function returns an empty list.
+*/
+QList<QNetworkConfiguration> QNetworkConfiguration::children() const
+{
+ QList<QNetworkConfiguration> results;
+
+ if (!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();
+
+ QNetworkConfigurationPrivatePointer p = i.value();
+
+ //if we have an invalid member get rid of it -> was deleted earlier on
+ {
+ QMutexLocker childLocker(&p->mutex);
+
+ if (!p->isValid) {
+ i.remove();
+ continue;
+ }
+ }
+
+ QNetworkConfiguration item;
+ item.d = p;
+ results << item;
+ }
+
+ return results;
+}
+
+/*!
+ \fn QString QNetworkConfiguration::bearerName() const
+ \deprecated
+
+ This function is deprecated. It is equivalent to calling bearerTypeName(), however
+ bearerType() should be used in preference.
+*/
+
+/*!
+ Returns the type of bearer used by this network configuration.
+
+ If the bearer type is \l {QNetworkConfiguration::BearerUnknown}{unknown} the bearerTypeName()
+ function can be used to retrieve a textural type name for the bearer.
+
+ An invalid network configuration always returns the BearerUnknown value.
+*/
+QNetworkConfiguration::BearerType QNetworkConfiguration::bearerType() const
+{
+ if (!isValid())
+ return BearerUnknown;
+
+ QMutexLocker locker(&d->mutex);
+
+ return d->bearerType;
+}
+
+/*!
+ Returns the type of bearer used by this network configuration as a string.
+
+ The string is not translated and therefore can not be shown to the user. The subsequent table
+ shows the fixed mappings between BearerType and the bearer type name for known types. If the
+ BearerType is unknown this function may return additional information if it is available;
+ otherwise an empty string will be returned.
+
+ \table
+ \header
+ \o BearerType
+ \o Value
+ \row
+ \o BearerUnknown
+ \o
+ \o The session is based on an unknown or unspecified bearer type. The value of the
+ string returned describes the bearer type.
+ \row
+ \o BearerEthernet
+ \o Ethernet
+ \row
+ \o BearerWLAN
+ \o WLAN
+ \row
+ \o Bearer2G
+ \o 2G
+ \row
+ \o BearerCDMA2000
+ \o CDMA2000
+ \row
+ \o BearerWCDMA
+ \o WCDMA
+ \row
+ \o BearerHSPA
+ \o HSPA
+ \row
+ \o BearerBluetooth
+ \o Bluetooth
+ \row
+ \o BearerWiMAX
+ \o WiMAX
+ \endtable
+
+ This function returns an empty string if this is an invalid configuration, a network
+ configuration of type \l QNetworkConfiguration::ServiceNetwork or
+ \l QNetworkConfiguration::UserChoice.
+
+ \sa bearerType()
+*/
+QString QNetworkConfiguration::bearerTypeName() const
+{
+ if (!isValid())
+ return QString();
+
+ QMutexLocker locker(&d->mutex);
+
+ if (d->type == QNetworkConfiguration::ServiceNetwork ||
+ d->type == QNetworkConfiguration::UserChoice)
+ return QString();
+
+ switch (d->bearerType) {
+ case BearerUnknown:
+ return d->bearerTypeName();
+ case BearerEthernet:
+ return QLatin1String("Ethernet");
+ case BearerWLAN:
+ return QLatin1String("WLAN");
+ case Bearer2G:
+ return QLatin1String("2G");
+ case BearerCDMA2000:
+ return QLatin1String("CDMA2000");
+ case BearerWCDMA:
+ return QLatin1String("WCDMA");
+ case BearerHSPA:
+ return QLatin1String("HSPA");
+ case BearerBluetooth:
+ return QLatin1String("Bluetooth");
+ case BearerWiMAX:
+ return QLatin1String("WiMAX");
+ }
+
+ return QLatin1String("Unknown");
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h
new file mode 100644
index 0000000000..2e8dadda25
--- /dev/null
+++ b/src/network/bearer/qnetworkconfiguration.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCONFIGURATION_H
+#define QNETWORKCONFIGURATION_H
+
+#ifndef QT_MOBILITY_BEARER
+# include <QtCore/qglobal.h>
+#else
+# include "qmobilityglobal.h"
+#endif
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+
+#if defined(Q_OS_WIN) && defined(interface)
+#undef interface
+#endif
+
+QT_BEGIN_HEADER
+
+#ifndef QT_MOBILITY_BEARER
+QT_BEGIN_NAMESPACE
+QT_MODULE(Network)
+#define QNetworkConfigurationExport Q_NETWORK_EXPORT
+#else
+QTM_BEGIN_NAMESPACE
+#define QNetworkConfigurationExport Q_BEARER_EXPORT
+#endif
+
+class QNetworkConfigurationPrivate;
+class QNetworkConfigurationExport QNetworkConfiguration
+{
+public:
+ QNetworkConfiguration();
+ QNetworkConfiguration(const QNetworkConfiguration& other);
+ QNetworkConfiguration &operator=(const QNetworkConfiguration &other);
+ ~QNetworkConfiguration();
+
+ bool operator==(const QNetworkConfiguration &other) const;
+ inline bool operator!=(const QNetworkConfiguration &other) const
+ { return !operator==(other); }
+
+ enum Type {
+ InternetAccessPoint = 0,
+ ServiceNetwork,
+ UserChoice,
+ Invalid
+ };
+
+ enum Purpose {
+ UnknownPurpose = 0,
+ PublicPurpose,
+ PrivatePurpose,
+ ServiceSpecificPurpose
+ };
+
+ enum StateFlag {
+ Undefined = 0x0000001,
+ Defined = 0x0000002,
+ Discovered = 0x0000006,
+ Active = 0x000000e
+ };
+ Q_DECLARE_FLAGS(StateFlags, StateFlag)
+
+#ifndef QT_MOBILITY_BEARER
+ enum BearerType {
+ BearerUnknown,
+ BearerEthernet,
+ BearerWLAN,
+ Bearer2G,
+ BearerCDMA2000,
+ BearerWCDMA,
+ BearerHSPA,
+ BearerBluetooth,
+ BearerWiMAX
+ };
+#endif
+
+ StateFlags state() const;
+ Type type() const;
+ Purpose purpose() const;
+
+#ifndef QT_MOBILITY_BEARER
+#ifdef QT_DEPRECATED
+ // Required to maintain source compatibility with Qt Mobility.
+ QT_DEPRECATED inline QString bearerName() const { return bearerTypeName(); }
+#endif
+ BearerType bearerType() const;
+ QString bearerTypeName() const;
+#else
+ QString bearerName() const;
+#endif
+
+ QString identifier() const;
+ bool isRoamingAvailable() const;
+ QList<QNetworkConfiguration> children() const;
+
+ QString name() const;
+ bool isValid() const;
+
+private:
+ friend class QNetworkConfigurationPrivate;
+ friend class QNetworkConfigurationManager;
+ friend class QNetworkConfigurationManagerPrivate;
+ friend class QNetworkSessionPrivate;
+ QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> d;
+};
+
+#ifndef QT_MOBILITY_BEARER
+QT_END_NAMESPACE
+#else
+QTM_END_NAMESPACE
+#endif
+
+QT_END_HEADER
+
+#endif // QNETWORKCONFIGURATION_H
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
new file mode 100644
index 0000000000..a38bc0b7a1
--- /dev/null
+++ b/src/network/bearer/qnetworkconfiguration_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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKCONFIGURATIONPRIVATE_H
+#define QNETWORKCONFIGURATIONPRIVATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworkconfiguration.h"
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfigurationPrivatePointer;
+class QNetworkConfigurationPrivate : public QSharedData
+{
+public:
+ 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();
+ }
+
+ virtual QString bearerTypeName() const
+ {
+ return QLatin1String("Unknown");
+ }
+
+ QMap<unsigned int, QNetworkConfigurationPrivatePointer> serviceNetworkMembers;
+
+ mutable QMutex mutex;
+
+ QString name;
+ QString id;
+
+ QNetworkConfiguration::StateFlags state;
+ QNetworkConfiguration::Type type;
+ QNetworkConfiguration::Purpose purpose;
+ QNetworkConfiguration::BearerType bearerType;
+
+ bool isValid;
+ bool roamingSupported;
+
+private:
+ Q_DISABLE_COPY(QNetworkConfigurationPrivate)
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKCONFIGURATIONPRIVATE_H
diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp
new file mode 100644
index 0000000000..21e64d963e
--- /dev/null
+++ b/src/network/bearer/qnetworksession.cpp
@@ -0,0 +1,752 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworksession.h"
+#include "qbearerengine_p.h"
+
+#include <QEventLoop>
+#include <QTimer>
+#include <QThread>
+
+#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
+
+/*!
+ \class QNetworkSession
+
+ \brief The QNetworkSession class provides control over the system's access points
+ and enables session management for cases when multiple clients access the same access point.
+
+ \since 4.7
+
+ \inmodule QtNetwork
+ \ingroup network
+
+ A QNetworkSession enables control over the system's network interfaces. The session's configuration
+ parameter are determined via the QNetworkConfiguration object to which it is bound. Depending on the
+ type of the session (single access point or service network) a session may be linked to one or more
+ network interfaces. By means of \l{open()}{opening} and \l{close()}{closing} of network sessions
+ a developer can start and stop the systems network interfaces. If the configuration represents
+ multiple access points (see \l QNetworkConfiguration::ServiceNetwork) more advanced features such as roaming may be supported.
+
+ QNetworkSession supports session management within the same process and depending on the platform's
+ capabilities may support out-of-process sessions. If the same
+ network configuration is used by multiple open sessions the underlying network interface is only terminated once
+ the last session has been closed.
+
+ \section1 Roaming
+
+ Applications may connect to the preferredConfigurationChanged() signal in order to
+ receive notifications when a more suitable access point becomes available.
+ In response to this signal the application must either initiate the roaming via migrate()
+ or ignore() the new access point. Once the session has roamed the
+ newConfigurationActivated() signal is emitted. The application may now test the
+ carrier and must either accept() or reject() it. The session will return to the previous
+ access point if the roaming was rejected. The subsequent state diagram depicts the required
+ state transitions.
+
+ \image roaming-states.png
+
+ Some platforms may distinguish forced roaming and application level roaming (ALR).
+ ALR implies that the application controls (via migrate(), ignore(), accept() and reject())
+ whether a network session can roam from one access point to the next. Such control is useful
+ if the application maintains stateful socket connections and wants to control the transition from
+ one interface to the next. Forced roaming implies that the system automatically roams to the next network without
+ consulting the application. This has the advantage that the application can make use of roaming features
+ without actually being aware of it. It is expected that the application detects that the underlying
+ socket is broken and automatically reconnects via the new network link.
+
+ If the platform supports both modes of roaming, an application indicates its preference
+ by connecting to the preferredConfigurationChanged() signal. Connecting to this signal means that
+ the application wants to take control over the roaming behavior and therefore implies application
+ level roaming. If the client does not connect to the preferredConfigurationChanged(), forced roaming
+ is used. If forced roaming is not supported the network session will not roam by default.
+
+ Some applications may want to suppress any form of roaming altogether. Possible use cases may be
+ high priority downloads or remote services which cannot handle a roaming enabled client. Clients
+ can suppress roaming by connecting to the preferredConfigurationChanged() signal and answer each
+ signal emission with ignore().
+
+ \sa QNetworkConfiguration, QNetworkConfigurationManager
+*/
+
+/*!
+ \enum QNetworkSession::State
+
+ This enum describes the connectivity state of the session. If the session is based on a
+ single access point configuration the state of the session is the same as the state of the
+ associated network interface.
+
+ \value Invalid The session is invalid due to an invalid configuration. This may
+ happen due to a removed access point or a configuration that was
+ invalid to begin with.
+ \value NotAvailable The session is based on a defined but not yet discovered QNetworkConfiguration
+ (see \l QNetworkConfiguration::StateFlag).
+ \value Connecting The network session is being established.
+ \value Connected The network session is connected. If the current process wishes to use this session
+ it has to register its interest by calling open(). A network session
+ is considered to be ready for socket operations if it isOpen() and connected.
+ \value Closing The network session is in the process of being shut down.
+ \value Disconnected The network session is not connected. The associated QNetworkConfiguration
+ has the state QNetworkConfiguration::Discovered.
+ \value Roaming The network session is roaming from one access point to another
+ access point.
+*/
+
+/*!
+ \enum QNetworkSession::SessionError
+
+ This enum describes the session errors that can occur.
+
+ \value UnknownSessionError An unidentified error occurred.
+ \value SessionAbortedError The session was aborted by the user or system.
+ \value RoamingError The session cannot roam to a new configuration.
+ \value OperationNotSupportedError The operation is not supported for current configuration.
+ \value InvalidConfigurationError The operation cannot currently be performed for the
+ current configuration.
+*/
+
+/*!
+ \fn void QNetworkSession::stateChanged(QNetworkSession::State state)
+
+ This signal is emitted whenever the state of the network session changes.
+ The \a state parameter is the new state.
+
+ \sa state()
+*/
+
+/*!
+ \fn void QNetworkSession::error(QNetworkSession::SessionError error)
+
+ This signal is emitted after an error occurred. The \a error parameter
+ describes the error that occurred.
+
+ \sa error(), errorString()
+*/
+
+/*!
+ \fn void QNetworkSession::preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless)
+
+ This signal is emitted when the preferred configuration/access point for the
+ session changes. Only sessions which are based on service network configurations
+ may emit this signal. \a config can be used to determine access point specific
+ details such as proxy settings and \a isSeamless indicates whether roaming will
+ break the sessions IP address.
+
+ As a consequence to this signal the application must either start the roaming process
+ by calling migrate() or choose to ignore() the new access point.
+
+ If the roaming process is non-seamless the IP address will change which means that
+ a socket becomes invalid. However seamless mobility can ensure that the local IP address
+ does not change. This is achieved by using a virtual IP address which is bound to the actual
+ link address. During the roaming process the virtual address is attached to the new link
+ address.
+
+ Some platforms may support the concept of Forced Roaming and Application Level Roaming (ALR).
+ Forced roaming implies that the platform may simply roam to a new configuration without
+ consulting applications. It is up to the application to detect the link layer loss and reestablish
+ its sockets. In contrast ALR provides the opportunity to prevent the system from roaming.
+ If this session is based on a configuration that supports roaming the application can choose
+ whether it wants to be consulted (ALR use case) by connecting to this signal. For as long as this signal
+ connection remains the session remains registered as a roaming stakeholder; otherwise roaming will
+ be enforced by the platform.
+
+ \sa migrate(), ignore(), QNetworkConfiguration::isRoamingAvailable()
+*/
+
+/*!
+ \fn void QNetworkSession::newConfigurationActivated()
+
+ This signal is emitted once the session has roamed to the new access point.
+ The application may reopen its socket and test the suitability of the new network link.
+ Subsequently it must either accept() or reject() the new access point.
+
+ \sa accept(), reject()
+*/
+
+/*!
+ \fn void QNetworkSession::opened()
+
+ This signal is emitted when the network session has been opened.
+
+ The underlying network interface will not be shut down as long as the session remains open.
+ Note that this feature is dependent on \l{QNetworkConfigurationManager::SystemSessionSupport}{system wide session support}.
+*/
+
+/*!
+ \fn void QNetworkSession::closed()
+
+ This signal is emitted when the network session has been closed.
+*/
+
+/*!
+ Constructs a session based on \a connectionConfig with the given \a parent.
+
+ \sa QNetworkConfiguration
+*/
+QNetworkSession::QNetworkSession(const QNetworkConfiguration &connectionConfig, QObject *parent)
+ : QObject(parent), d(0)
+{
+ // invalid configuration
+ 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;
+ }
+ }
+ }
+
+ qRegisterMetaType<QNetworkSession::State>();
+ qRegisterMetaType<QNetworkSession::SessionError>();
+}
+
+/*!
+ Frees the resources associated with the QNetworkSession object.
+*/
+QNetworkSession::~QNetworkSession()
+{
+ delete d;
+}
+
+/*!
+ Creates an open session which increases the session counter on the underlying network interface.
+ The system will not terminate a network interface until the session reference counter reaches zero.
+ Therefore an open session allows an application to register its use of the interface.
+
+ As a result of calling open() the interface will be started if it is not connected/up yet.
+ Some platforms may not provide support for out-of-process sessions. On such platforms the session
+ counter ignores any sessions held by another process. The platform capabilities can be
+ detected via QNetworkConfigurationManager::capabilities().
+
+ Note that this call is asynchronous. Depending on the outcome of this call the results can be enquired
+ by connecting to the stateChanged(), opened() or error() signals.
+
+ It is not a requirement to open a session in order to monitor the underlying network interface.
+
+ \sa close(), stop(), isOpen()
+*/
+void QNetworkSession::open()
+{
+ if (d)
+ d->open();
+ else
+ emit error(InvalidConfigurationError);
+}
+
+/*!
+ Waits until the session has been opened, up to \a msecs milliseconds. If the session has been opened, this
+ function returns true; otherwise it returns false. In the case where it returns false, you can call error()
+ to determine the cause of the error.
+
+ The following example waits up to one second for the session to be opened:
+
+ \code
+ session->open();
+ if (session->waitForOpened(1000))
+ qDebug("Open!");
+ \endcode
+
+ If \a msecs is -1, this function will not time out.
+
+ \sa open(), error()
+*/
+bool QNetworkSession::waitForOpened(int msecs)
+{
+ if (!d)
+ return false;
+
+ if (d->isOpen)
+ return true;
+
+ if (!(d->state == Connecting || d->state == Connected)) {
+ return false;
+ }
+
+ 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()));
+
+ // enter the event loop and wait for opened/error/timeout
+ loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
+
+ return d->isOpen;
+}
+
+/*!
+ Decreases the session counter on the associated network configuration. If the session counter reaches zero
+ the active network interface is shut down. This also means that state() will only change from \l Connected to
+ \l Disconnected if the current session was the last open session.
+
+ If the platform does not support out-of-process sessions calling this function does not stop the
+ interface. In this case \l{stop()} has to be used to force a shut down.
+ The platform capabilities can be detected via QNetworkConfigurationManager::capabilities().
+
+ Note that this call is asynchronous. Depending on the outcome of this call the results can be enquired
+ by connecting to the stateChanged(), opened() or error() signals.
+
+ \sa open(), stop(), isOpen()
+*/
+void QNetworkSession::close()
+{
+ if (d)
+ d->close();
+}
+
+/*!
+ Invalidates all open sessions against the network interface and therefore stops the
+ underlying network interface. This function always changes the session's state() flag to
+ \l Disconnected.
+
+ On Symbian platform, a 'NetworkControl' capability is required for
+ full interface-level stop (without the capability, only the current session is stopped).
+
+ \sa open(), close()
+*/
+void QNetworkSession::stop()
+{
+ if (d)
+ d->stop();
+}
+
+/*!
+ Returns the QNetworkConfiguration that this network session object is based on.
+
+ \sa QNetworkConfiguration
+*/
+QNetworkConfiguration QNetworkSession::configuration() const
+{
+ return d ? d->publicConfig : QNetworkConfiguration();
+}
+
+#ifndef QT_NO_NETWORKINTERFACE
+/*!
+ Returns the network interface that is used by this session.
+
+ This function only returns a valid QNetworkInterface when this session is \l Connected.
+
+ The returned interface may change as a result of a roaming process.
+
+ Note: this function does not work in Symbian emulator due to the way the
+ connectivity is emulated on Windows.
+
+ \sa state()
+*/
+QNetworkInterface QNetworkSession::interface() const
+{
+ return d ? d->currentInterface() : QNetworkInterface();
+}
+#endif
+
+/*!
+ Returns true if this session is open. If the number of all open sessions is greater than
+ zero the underlying network interface will remain connected/up.
+
+ The session can be controlled via open() and close().
+*/
+bool QNetworkSession::isOpen() const
+{
+ return d ? d->isOpen : false;
+}
+
+/*!
+ Returns the state of the session.
+
+ If the session is based on a single access point configuration the state of the
+ session is the same as the state of the associated network interface. Therefore
+ a network session object can be used to monitor network interfaces.
+
+ A \l QNetworkConfiguration::ServiceNetwork based session summarizes the state of all its children
+ and therefore returns the \l Connected state if at least one of the service network's
+ \l {QNetworkConfiguration::children()}{children()} configurations is active.
+
+ Note that it is not required to hold an open session in order to obtain the network interface state.
+ A connected but closed session may be used to monitor network interfaces whereas an open and connected
+ session object may prevent the network interface from being shut down.
+
+ \sa error(), stateChanged()
+*/
+QNetworkSession::State QNetworkSession::state() const
+{
+ return d ? d->state : QNetworkSession::Invalid;
+}
+
+/*!
+ Returns the type of error that last occurred.
+
+ \sa state(), errorString()
+*/
+QNetworkSession::SessionError QNetworkSession::error() const
+{
+ return d ? d->error() : InvalidConfigurationError;
+}
+
+/*!
+ Returns a human-readable description of the last device error that
+ occurred.
+
+ \sa error()
+*/
+QString QNetworkSession::errorString() const
+{
+ return d ? d->errorString() : tr("Invalid configuration.");
+}
+
+/*!
+ Returns the value for property \a key.
+
+ A network session can have properties attached which may describe the session in more details.
+ This function can be used to gain access to those properties.
+
+ The following property keys are guaranteed to be specified on all platforms:
+
+ \table
+ \header
+ \o Key \o Description
+ \row
+ \o ActiveConfiguration
+ \o If the session \l isOpen() this property returns the identifier of the
+ QNetworkConfiguration that is used by this session; otherwise an empty string.
+
+ The main purpose of this key is to determine which Internet access point is used
+ if the session is based on a \l{QNetworkConfiguration::ServiceNetwork}{ServiceNetwork}.
+ The following code snippet highlights the difference:
+ \code
+ QNetworkConfigurationManager mgr;
+ QNetworkConfiguration ap = mgr.defaultConfiguration();
+ QNetworkSession *session = new QNetworkSession(ap);
+ ... //code activates session
+
+ QString ident = session->sessionProperty("ActiveConfiguration").toString();
+ if ( ap.type() == QNetworkConfiguration::ServiceNetwork ) {
+ Q_ASSERT( ap.identifier() != ident );
+ Q_ASSERT( ap.children().contains( mgr.configurationFromIdentifier(ident) ) );
+ } else if ( ap.type() == QNetworkConfiguration::InternetAccessPoint ) {
+ Q_ASSERT( ap.identifier() == ident );
+ }
+ \endcode
+ \row
+ \o UserChoiceConfiguration
+ \o If the session \l isOpen() and is bound to a QNetworkConfiguration of type
+ UserChoice, this property returns the identifier of the QNetworkConfiguration that the
+ configuration resolved to when \l open() was called; otherwise an empty string.
+
+ The purpose of this key is to determine the real QNetworkConfiguration that the
+ session is using. This key is different from \e ActiveConfiguration in that
+ this key may return an identifier for either a
+ \l {QNetworkConfiguration::ServiceNetwork}{service network} or a
+ \l {QNetworkConfiguration::InternetAccessPoint}{Internet access points} configurations,
+ whereas \e ActiveConfiguration always returns identifiers to
+ \l {QNetworkConfiguration::InternetAccessPoint}{Internet access points} configurations.
+ \row
+ \o ConnectInBackground
+ \o Setting this property to \e true before calling \l open() implies that the connection attempt
+ is made but if no connection can be established, the user is not connsulted and asked to select
+ a suitable connection. This property is not set by default and support for it depends on the platform.
+
+ \row
+ \o AutoCloseSessionTimeout
+ \o If the session requires polling to keep its state up to date, this property holds
+ the timeout in milliseconds before the session will automatically close. If the
+ value of this property is -1 the session will not automatically close. This property
+ is set to -1 by default.
+
+ The purpose of this property is to minimize resource use on platforms that use
+ polling to update the state of the session. Applications can set the value of this
+ property to the desired timeout before the session is closed. In response to the
+ closed() signal the network session should be deleted to ensure that all polling is
+ stopped. The session can then be recreated once it is required again. This property
+ has no effect for sessions that do not require polling.
+ \endtable
+*/
+QVariant QNetworkSession::sessionProperty(const QString &key) const
+{
+ if (!d || !d->publicConfig.isValid())
+ return QVariant();
+
+ if (key == QLatin1String("ActiveConfiguration"))
+ return d->isOpen ? d->activeConfig.identifier() : QString();
+
+ if (key == QLatin1String("UserChoiceConfiguration")) {
+ if (!d->isOpen || d->publicConfig.type() != QNetworkConfiguration::UserChoice)
+ return QString();
+
+ if (d->serviceConfig.isValid())
+ return d->serviceConfig.identifier();
+ else
+ return d->activeConfig.identifier();
+ }
+
+ return d->sessionProperty(key);
+}
+
+/*!
+ Sets the property \a value on the session. The property is identified using
+ \a key. Removing an already set property can be achieved by passing an
+ invalid QVariant.
+
+ Note that the \e UserChoiceConfiguration and \e ActiveConfiguration
+ properties are read only and cannot be changed using this method.
+*/
+void QNetworkSession::setSessionProperty(const QString &key, const QVariant &value)
+{
+ if (!d)
+ return;
+
+ if (key == QLatin1String("ActiveConfiguration") ||
+ key == QLatin1String("UserChoiceConfiguration")) {
+ return;
+ }
+
+ d->setSessionProperty(key, value);
+}
+
+/*!
+ Instructs the session to roam to the new access point. The old access point remains active
+ until the application calls accept().
+
+ The newConfigurationActivated() signal is emitted once roaming has been completed.
+
+ \sa accept()
+*/
+void QNetworkSession::migrate()
+{
+ if (d)
+ d->migrate();
+}
+
+/*!
+ This function indicates that the application does not wish to roam the session.
+
+ \sa migrate()
+*/
+void QNetworkSession::ignore()
+{
+ // Needed on at least Symbian platform: the roaming must be explicitly
+ // ignore()'d or migrate()'d
+ if (d)
+ d->ignore();
+}
+
+/*!
+ Instructs the session to permanently accept the new access point. Once this function
+ has been called the session may not return to the old access point.
+
+ The old access point may be closed in the process if there are no other network sessions for it.
+ Therefore any open socket that still uses the old access point
+ may become unusable and should be closed before completing the migration.
+*/
+void QNetworkSession::accept()
+{
+ if (d)
+ d->accept();
+}
+
+/*!
+ The new access point is not suitable for the application. By calling this function the
+ session returns to the previous access point/configuration. This action may invalidate
+ any socket that has been created via the not desired access point.
+
+ \sa accept()
+*/
+void QNetworkSession::reject()
+{
+ if (d)
+ d->reject();
+}
+
+
+/*!
+ Returns the amount of data sent in bytes; otherwise 0.
+
+ This field value includes the usage across all open network
+ sessions which use the same network interface.
+
+ If the session is based on a service network configuration the number of
+ sent bytes across all active member configurations are returned.
+
+ This function may not always be supported on all platforms and returns 0.
+ The platform capability can be detected via QNetworkConfigurationManager::DataStatistics.
+
+ \note On some platforms this function may run the main event loop.
+*/
+quint64 QNetworkSession::bytesWritten() const
+{
+ return d ? d->bytesWritten() : Q_UINT64_C(0);
+}
+
+/*!
+ Returns the amount of data received in bytes; otherwise 0.
+
+ This field value includes the usage across all open network
+ sessions which use the same network interface.
+
+ If the session is based on a service network configuration the number of
+ sent bytes across all active member configurations are returned.
+
+ This function may not always be supported on all platforms and returns 0.
+ The platform capability can be detected via QNetworkConfigurationManager::DataStatistics.
+
+ \note On some platforms this function may run the main event loop.
+*/
+quint64 QNetworkSession::bytesReceived() const
+{
+ return d ? d->bytesReceived() : Q_UINT64_C(0);
+}
+
+/*!
+ Returns the number of seconds that the session has been active.
+*/
+quint64 QNetworkSession::activeTime() const
+{
+ return d ? d->activeTime() : Q_UINT64_C(0);
+}
+
+/*!
+ \internal
+
+ This function is required to detect whether the client wants to control
+ the roaming process. If he connects to preferredConfigurationChanged() signal
+ he intends to influence it. Otherwise QNetworkSession always roams
+ without registering this session as a stakeholder in the roaming process.
+
+ For more details check the Forced vs ALR roaming section in the QNetworkSession
+ class description.
+*/
+void QNetworkSession::connectNotify(const char *signal)
+{
+ QObject::connectNotify(signal);
+
+ 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);
+}
+
+/*!
+ \internal
+
+ This function is called when the client disconnects from the
+ preferredConfigurationChanged() signal.
+
+ \sa connectNotify()
+*/
+void QNetworkSession::disconnectNotify(const char *signal)
+{
+ QObject::disconnectNotify(signal);
+
+ 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
+
+#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qnetworksession.h b/src/network/bearer/qnetworksession.h
new file mode 100644
index 0000000000..688f37eba4
--- /dev/null
+++ b/src/network/bearer/qnetworksession.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKSESSION_H
+#define QNETWORKSESSION_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+#include <QtNetwork/qnetworkinterface.h>
+#include <QtCore/qvariant.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+#if defined(Q_OS_WIN) && defined(interface)
+#undef interface
+#endif
+
+QT_BEGIN_HEADER
+
+#ifndef QT_MOBILITY_BEARER
+#include <QtCore/qshareddata.h>
+QT_BEGIN_NAMESPACE
+QT_MODULE(Network)
+#define QNetworkSessionExport Q_NETWORK_EXPORT
+#else
+#include "qmobilityglobal.h"
+QTM_BEGIN_NAMESPACE
+#define QNetworkSessionExport Q_BEARER_EXPORT
+#endif
+
+class QNetworkSessionPrivate;
+class QNetworkSessionExport QNetworkSession : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum State {
+ Invalid = 0,
+ NotAvailable,
+ Connecting,
+ Connected,
+ Closing,
+ Disconnected,
+ Roaming
+ };
+
+ enum SessionError {
+ UnknownSessionError = 0,
+ SessionAbortedError,
+ RoamingError,
+ OperationNotSupportedError,
+ InvalidConfigurationError
+ };
+
+ explicit QNetworkSession(const QNetworkConfiguration &connConfig, QObject *parent = 0);
+ virtual ~QNetworkSession();
+
+ bool isOpen() const;
+ QNetworkConfiguration configuration() const;
+#ifndef QT_NO_NETWORKINTERFACE
+ QNetworkInterface interface() const;
+#endif
+
+ State state() const;
+ SessionError error() const;
+ QString errorString() const;
+ QVariant sessionProperty(const QString &key) const;
+ void setSessionProperty(const QString &key, const QVariant &value);
+
+ quint64 bytesWritten() const;
+ quint64 bytesReceived() const;
+ quint64 activeTime() const;
+
+ bool waitForOpened(int msecs = 30000);
+
+public Q_SLOTS:
+ void open();
+ void close();
+ void stop();
+
+ //roaming related slots
+ void migrate();
+ void ignore();
+ void accept();
+ void reject();
+
+Q_SIGNALS:
+ void stateChanged(QNetworkSession::State);
+ void opened();
+ void closed();
+ void error(QNetworkSession::SessionError);
+ void preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless);
+ void newConfigurationActivated();
+
+protected:
+ virtual void connectNotify(const char *signal);
+ virtual void disconnectNotify(const char *signal);
+
+private:
+ friend class QNetworkSessionPrivate;
+ QNetworkSessionPrivate *d;
+};
+
+#ifndef QT_MOBILITY_BEARER
+QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QNetworkSession::State)
+Q_DECLARE_METATYPE(QNetworkSession::SessionError)
+#else
+QTM_END_NAMESPACE
+#endif
+
+QT_END_HEADER
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif // QNETWORKSESSION_H
diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h
new file mode 100644
index 0000000000..a92b7ce315
--- /dev/null
+++ b/src/network/bearer/qnetworksession_p.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKSESSIONPRIVATE_H
+#define QNETWORKSESSIONPRIVATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworksession.h"
+#include "qnetworkconfiguration_p.h"
+#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
+{
+ Q_OBJECT
+
+ friend class QNetworkSession;
+
+public:
+ 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
+ //a session). Also this function should take care of
+ //notification hooks to discover future state changes.
+ virtual void syncStateWithInterface() = 0;
+
+#ifndef QT_NO_NETWORKINTERFACE
+ virtual QNetworkInterface currentInterface() const = 0;
+#endif
+ virtual QVariant sessionProperty(const QString &key) const = 0;
+ virtual void setSessionProperty(const QString &key, const QVariant &value) = 0;
+
+ virtual void open() = 0;
+ virtual void close() = 0;
+ virtual void stop() = 0;
+
+ virtual void setALREnabled(bool /*enabled*/) {}
+ virtual void migrate() = 0;
+ virtual void accept() = 0;
+ virtual void ignore() = 0;
+ virtual void reject() = 0;
+
+ virtual QString errorString() const = 0; //must return translated string
+ virtual QNetworkSession::SessionError error() const = 0;
+
+ virtual quint64 bytesWritten() const = 0;
+ virtual quint64 bytesReceived() const = 0;
+ virtual quint64 activeTime() const = 0;
+
+#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
+ {
+ return config.d;
+ }
+
+ inline void setPrivateConfiguration(QNetworkConfiguration &config,
+ QNetworkConfigurationPrivatePointer ptr) const
+ {
+ config.d = ptr;
+ }
+
+Q_SIGNALS:
+ //releases any pending waitForOpened() calls
+ void quitPendingWaitsForOpened();
+
+ void error(QNetworkSession::SessionError error);
+ void stateChanged(QNetworkSession::State state);
+ void closed();
+ void newConfigurationActivated();
+ void preferredConfigurationChanged(const QNetworkConfiguration &config, bool isSeamless);
+
+protected:
+ QNetworkSession *q;
+
+ // The config set on QNetworkSession.
+ QNetworkConfiguration publicConfig;
+
+ // If publicConfig is a ServiceNetwork this is a copy of publicConfig.
+ // If publicConfig is an UserChoice that is resolved to a ServiceNetwork this is the actual
+ // ServiceNetwork configuration.
+ QNetworkConfiguration serviceConfig;
+
+ // This is the actual active configuration currently in use by the session.
+ // Either a copy of publicConfig or one of serviceConfig.children().
+ QNetworkConfiguration activeConfig;
+
+ QNetworkSession::State state;
+ bool isOpen;
+
+ QMutex mutex;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSharedPointer<QNetworkSession>)
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif // QNETWORKSESSIONPRIVATE_H
diff --git a/src/network/bearer/qsharednetworksession.cpp b/src/network/bearer/qsharednetworksession.cpp
new file mode 100644
index 0000000000..fcb0128817
--- /dev/null
+++ b/src/network/bearer/qsharednetworksession.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsharednetworksession_p.h"
+#include "qbearerengine_p.h"
+#include <QThreadStorage>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+QThreadStorage<QSharedNetworkSessionManager *> tls;
+
+inline QSharedNetworkSessionManager* sharedNetworkSessionManager()
+{
+ QSharedNetworkSessionManager* rv = tls.localData();
+ if (!rv) {
+ rv = new QSharedNetworkSessionManager;
+ tls.setLocalData(rv);
+ }
+ return rv;
+}
+
+static void doDeleteLater(QObject* obj)
+{
+ obj->deleteLater();
+}
+
+QSharedPointer<QNetworkSession> QSharedNetworkSessionManager::getSession(QNetworkConfiguration config)
+{
+ QSharedNetworkSessionManager *m(sharedNetworkSessionManager());
+ //if already have a session, return it
+ if (m->sessions.contains(config)) {
+ QSharedPointer<QNetworkSession> p = m->sessions.value(config).toStrongRef();
+ if (!p.isNull())
+ return p;
+ }
+ //otherwise make one
+ QSharedPointer<QNetworkSession> session(new QNetworkSession(config), doDeleteLater);
+ m->sessions[config] = session;
+ return session;
+}
+
+void QSharedNetworkSessionManager::setSession(QNetworkConfiguration config, QSharedPointer<QNetworkSession> session)
+{
+ QSharedNetworkSessionManager *m(sharedNetworkSessionManager());
+ m->sessions[config] = session;
+}
+
+uint qHash(const QNetworkConfiguration& config)
+{
+ return ((uint)config.type()) | (((uint)config.bearerType()) << 8) | (((uint)config.purpose()) << 16);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
diff --git a/src/network/bearer/qsharednetworksession_p.h b/src/network/bearer/qsharednetworksession_p.h
new file mode 100644
index 0000000000..25b4ec2357
--- /dev/null
+++ b/src/network/bearer/qsharednetworksession_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHAREDNETWORKSESSIONPRIVATE_H
+#define QSHAREDNETWORKSESSIONPRIVATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworksession.h"
+#include "qnetworkconfiguration.h"
+#include <QHash>
+#include <QSharedPointer>
+#include <QWeakPointer>
+#include <QMutex>
+
+#ifndef QT_NO_BEARERMANAGEMENT
+
+QT_BEGIN_NAMESPACE
+
+uint qHash(const QNetworkConfiguration& config);
+
+class QSharedNetworkSessionManager
+{
+public:
+ static QSharedPointer<QNetworkSession> getSession(QNetworkConfiguration config);
+ static void setSession(QNetworkConfiguration config, QSharedPointer<QNetworkSession> session);
+private:
+ QHash<QNetworkConfiguration, QWeakPointer<QNetworkSession> > sessions;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_BEARERMANAGEMENT
+
+#endif //QSHAREDNETWORKSESSIONPRIVATE_H
+
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
new file mode 100644
index 0000000000..bb98305173
--- /dev/null
+++ b/src/network/kernel/kernel.pri
@@ -0,0 +1,34 @@
+# Qt network kernel module
+
+PRECOMPILED_HEADER = ../corelib/global/qt_pch.h
+INCLUDEPATH += $$PWD
+
+HEADERS += kernel/qauthenticator.h \
+ kernel/qauthenticator_p.h \
+ kernel/qhostaddress.h \
+ kernel/qhostinfo.h \
+ kernel/qhostinfo_p.h \
+ kernel/qurlinfo.h \
+ kernel/qnetworkproxy.h \
+ kernel/qnetworkinterface.h \
+ kernel/qnetworkinterface_p.h
+
+SOURCES += kernel/qauthenticator.cpp \
+ kernel/qhostaddress.cpp \
+ kernel/qhostinfo.cpp \
+ kernel/qurlinfo.cpp \
+ kernel/qnetworkproxy.cpp \
+ kernel/qnetworkinterface.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
+else:win32:SOURCES += kernel/qnetworkproxy_win.cpp
+else:symbian:SOURCES += kernel/qnetworkproxy_symbian.cpp
+else:SOURCES += kernel/qnetworkproxy_generic.cpp
+
+symbian: LIBS += -lcommsdat
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
new file mode 100644
index 0000000000..d61d3b7fa9
--- /dev/null
+++ b/src/network/kernel/qauthenticator.cpp
@@ -0,0 +1,1428 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qauthenticator.h>
+#include <qauthenticator_p.h>
+#include <qdebug.h>
+#include <qhash.h>
+#include <qbytearray.h>
+#include <qcryptographichash.h>
+#include <qhttp.h>
+#include <qiodevice.h>
+#include <qdatastream.h>
+#include <qendian.h>
+#include <qstring.h>
+#include <qdatetime.h>
+
+//#define NTLMV1_CLIENT
+
+QT_BEGIN_NAMESPACE
+
+#ifdef NTLMV1_CLIENT
+#include "../../3rdparty/des/des.cpp"
+#endif
+
+static QByteArray qNtlmPhase1();
+static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
+
+/*!
+ \class QAuthenticator
+ \brief The QAuthenticator class provides an authentication object.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QAuthenticator class is usually used in the
+ \l{QNetworkAccessManager::}{authenticationRequired()} and
+ \l{QNetworkAccessManager::}{proxyAuthenticationRequired()} signals of QNetworkAccessManager and
+ QAbstractSocket. The class provides a way to pass back the required
+ authentication information to the socket when accessing services that
+ require authentication.
+
+ QAuthenticator supports the following authentication methods:
+ \list
+ \o Basic
+ \o NTLM version 1
+ \o Digest-MD5
+ \endlist
+
+ Note that, in particular, NTLM version 2 is not supported.
+
+ \section1 Options
+
+ In addition to the username and password required for authentication, a
+ QAuthenticator object can also contain additional options. The
+ options() function can be used to query incoming options sent by
+ the server; the setOption() function can
+ be used to set outgoing options, to be processed by the authenticator
+ calculation. The options accepted and provided depend on the authentication
+ type (see method()).
+
+ The following tables list known incoming options as well as accepted
+ outgoing options. The list of incoming options is not exhaustive, since
+ servers may include additional information at any time. The list of
+ outgoing options is exhaustive, however, and no unknown options will be
+ treated or sent back to the server.
+
+ \section2 Basic
+
+ \table
+ \header \o Option \o Direction \o Description
+ \row \o \tt{realm} \o Incoming \o Contains the realm of the authentication, the same as realm()
+ \endtable
+
+ The Basic authentication mechanism supports no outgoing options.
+
+ \section2 NTLM version 1
+
+ The NTLM authentication mechanism currently supports no incoming or outgoing options.
+
+ \section2 Digest-MD5
+
+ \table
+ \header \o Option \o Direction \o Description
+ \row \o \tt{realm} \o Incoming \o Contains the realm of the authentication, the same as realm()
+ \endtable
+
+ The Digest-MD5 authentication mechanism supports no outgoing options.
+
+ \sa QSslSocket
+*/
+
+
+/*!
+ Constructs an empty authentication object
+*/
+QAuthenticator::QAuthenticator()
+ : d(0)
+{
+}
+
+/*!
+ Destructs the object
+*/
+QAuthenticator::~QAuthenticator()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QAuthenticator::QAuthenticator(const QAuthenticator &other)
+ : d(other.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Assigns the contents of \a other to this authenticator.
+*/
+QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
+{
+ if (d == other.d)
+ return *this;
+
+ if (d && !d->ref.deref())
+ delete d;
+
+ d = other.d;
+ if (d)
+ d->ref.ref();
+ return *this;
+}
+
+/*!
+ Returns true if this authenticator is identical to \a other; otherwise
+ returns false.
+*/
+bool QAuthenticator::operator==(const QAuthenticator &other) const
+{
+ if (d == other.d)
+ return true;
+ return d->user == other.d->user
+ && d->password == other.d->password
+ && d->realm == other.d->realm
+ && d->method == other.d->method
+ && d->options == other.d->options;
+}
+
+/*!
+ \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const
+
+ Returns true if this authenticator is different from \a other; otherwise
+ returns false.
+*/
+
+/*!
+ returns the user used for authentication.
+*/
+QString QAuthenticator::user() const
+{
+ return d ? d->user : QString();
+}
+
+/*!
+ Sets the \a user used for authentication.
+*/
+void QAuthenticator::setUser(const QString &user)
+{
+ detach();
+ int separatorPosn = 0;
+
+ switch(d->method) {
+ case QAuthenticatorPrivate::Ntlm:
+ if((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) {
+ //domain name is present
+ d->realm.clear();
+ d->userDomain = user.left(separatorPosn);
+ d->extractedUser = user.mid(separatorPosn + 1);
+ d->user = user;
+ } else if((separatorPosn = user.indexOf(QLatin1String("@"))) != -1) {
+ //domain name is present
+ d->realm.clear();
+ d->userDomain = user.left(separatorPosn);
+ d->extractedUser = user.left(separatorPosn);
+ d->user = user;
+ } else {
+ d->extractedUser = user;
+ d->user = user;
+ d->realm.clear();
+ d->userDomain.clear();
+ }
+ break;
+ default:
+ d->user = user;
+ d->userDomain.clear();
+ break;
+ }
+}
+
+/*!
+ returns the password used for authentication.
+*/
+QString QAuthenticator::password() const
+{
+ return d ? d->password : QString();
+}
+
+/*!
+ Sets the \a password used for authentication.
+*/
+void QAuthenticator::setPassword(const QString &password)
+{
+ detach();
+ d->password = password;
+}
+
+/*!
+ \internal
+*/
+void QAuthenticator::detach()
+{
+ if (!d) {
+ d = new QAuthenticatorPrivate;
+ d->ref = 1;
+ return;
+ }
+
+ qAtomicDetach(d);
+ d->phase = QAuthenticatorPrivate::Start;
+}
+
+/*!
+ returns the realm requiring authentication.
+*/
+QString QAuthenticator::realm() const
+{
+ return d ? d->realm : QString();
+}
+
+/*!
+ \since 4.7
+ Returns the value related to option \a opt if it was set by the server.
+ See \l{QAuthenticator#Options} for more information on incoming options.
+ If option \a opt isn't found, an invalid QVariant will be returned.
+
+ \sa options(), QAuthenticator#Options
+*/
+QVariant QAuthenticator::option(const QString &opt) const
+{
+ return d ? d->options.value(opt) : QVariant();
+}
+
+/*!
+ \since 4.7
+ Returns all incoming options set in this QAuthenticator object by parsing
+ the server reply. See \l{QAuthenticator#Options} for more information
+ on incoming options.
+
+ \sa option(), QAuthenticator#Options
+*/
+QVariantHash QAuthenticator::options() const
+{
+ return d ? d->options : QVariantHash();
+}
+
+/*!
+ \since 4.7
+
+ Sets the outgoing option \a opt to value \a value.
+ See \l{QAuthenticator#Options} for more information on outgoing options.
+
+ \sa options(), option(), QAuthenticator#Options
+*/
+void QAuthenticator::setOption(const QString &opt, const QVariant &value)
+{
+ detach();
+ d->options.insert(opt, value);
+}
+
+
+/*!
+ Returns true if the authenticator is null.
+*/
+bool QAuthenticator::isNull() const
+{
+ return !d;
+}
+
+QAuthenticatorPrivate::QAuthenticatorPrivate()
+ : ref(0)
+ , method(None)
+ , phase(Start)
+ , nonceCount(0)
+{
+ cnonce = QCryptographicHash::hash(QByteArray::number(qrand(), 16) + QByteArray::number(qrand(), 16),
+ QCryptographicHash::Md5).toHex();
+ nonceCount = 0;
+}
+
+#ifndef QT_NO_HTTP
+void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy)
+{
+ const QList<QPair<QString, QString> > values = header.values();
+ QList<QPair<QByteArray, QByteArray> > rawValues;
+
+ QList<QPair<QString, QString> >::const_iterator it, end;
+ for (it = values.constBegin(), end = values.constEnd(); it != end; ++it)
+ rawValues.append(qMakePair(it->first.toLatin1(), it->second.toUtf8()));
+
+ // continue in byte array form
+ parseHttpResponse(rawValues, isProxy);
+}
+#endif
+
+void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy)
+{
+ const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
+
+ method = None;
+ /*
+ Fun from the HTTP 1.1 specs, that we currently ignore:
+
+ User agents are advised to take special care in parsing the WWW-
+ Authenticate field value as it might contain more than one challenge,
+ or if more than one WWW-Authenticate header field is provided, the
+ contents of a challenge itself can contain a comma-separated list of
+ authentication parameters.
+ */
+
+ QByteArray headerVal;
+ for (int i = 0; i < values.size(); ++i) {
+ const QPair<QByteArray, QByteArray> &current = values.at(i);
+ if (current.first.toLower() != search)
+ continue;
+ QByteArray str = current.second.toLower();
+ if (method < Basic && str.startsWith("basic")) {
+ method = Basic;
+ headerVal = current.second.mid(6);
+ } else if (method < Ntlm && str.startsWith("ntlm")) {
+ method = Ntlm;
+ headerVal = current.second.mid(5);
+ } else if (method < DigestMd5 && str.startsWith("digest")) {
+ method = DigestMd5;
+ headerVal = current.second.mid(7);
+ }
+ }
+
+ challenge = headerVal.trimmed();
+ QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
+
+ switch(method) {
+ case Basic:
+ if(realm.isEmpty())
+ this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ if (user.isEmpty())
+ phase = Done;
+ break;
+ case Ntlm:
+ // #### extract from header
+ break;
+ case DigestMd5: {
+ if(realm.isEmpty())
+ this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ if (options.value("stale").toLower() == "true")
+ phase = Start;
+ if (user.isEmpty())
+ phase = Done;
+ break;
+ }
+ default:
+ realm.clear();
+ challenge = QByteArray();
+ phase = Invalid;
+ }
+}
+
+QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
+{
+ QByteArray response;
+ const char *methodString = 0;
+ switch(method) {
+ case QAuthenticatorPrivate::None:
+ methodString = "";
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Plain:
+ response = '\0' + user.toUtf8() + '\0' + password.toUtf8();
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Basic:
+ methodString = "Basic ";
+ response = user.toLatin1() + ':' + password.toLatin1();
+ response = response.toBase64();
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Login:
+ if (challenge.contains("VXNlciBOYW1lAA==")) {
+ response = user.toUtf8().toBase64();
+ phase = Phase2;
+ } else if (challenge.contains("UGFzc3dvcmQA")) {
+ response = password.toUtf8().toBase64();
+ phase = Done;
+ }
+ break;
+ case QAuthenticatorPrivate::CramMd5:
+ break;
+ case QAuthenticatorPrivate::DigestMd5:
+ methodString = "Digest ";
+ response = digestMd5Response(challenge, requestMethod, path);
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Ntlm:
+ methodString = "NTLM ";
+ if (challenge.isEmpty()) {
+ response = qNtlmPhase1().toBase64();
+ if (user.isEmpty())
+ phase = Done;
+ else
+ phase = Phase2;
+ } else {
+ response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
+ phase = Done;
+ }
+
+ break;
+ }
+ return QByteArray(methodString) + response;
+}
+
+
+// ---------------------------- Digest Md5 code ----------------------------------------
+
+QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge)
+{
+ QHash<QByteArray, QByteArray> options;
+ // parse the challenge
+ const char *d = challenge.constData();
+ const char *end = d + challenge.length();
+ while (d < end) {
+ while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
+ ++d;
+ const char *start = d;
+ while (d < end && *d != '=')
+ ++d;
+ QByteArray key = QByteArray(start, d - start);
+ ++d;
+ if (d >= end)
+ break;
+ bool quote = (*d == '"');
+ if (quote)
+ ++d;
+ if (d >= end)
+ break;
+ start = d;
+ QByteArray value;
+ while (d < end) {
+ bool backslash = false;
+ if (*d == '\\' && d < end - 1) {
+ ++d;
+ backslash = true;
+ }
+ if (!backslash) {
+ if (quote) {
+ if (*d == '"')
+ break;
+ } else {
+ if (*d == ',')
+ break;
+ }
+ }
+ value += *d;
+ ++d;
+ }
+ while (d < end && *d != ',')
+ ++d;
+ ++d;
+ options[key] = value;
+ }
+
+ QByteArray qop = options.value("qop");
+ if (!qop.isEmpty()) {
+ QList<QByteArray> qopoptions = qop.split(',');
+ if (!qopoptions.contains("auth"))
+ return QHash<QByteArray, QByteArray>();
+ // #### can't do auth-int currently
+// if (qop.contains("auth-int"))
+// qop = "auth-int";
+// else if (qop.contains("auth"))
+// qop = "auth";
+// else
+// qop = QByteArray();
+ options["qop"] = "auth";
+ }
+
+ return options;
+}
+
+/*
+ Digest MD5 implementation
+
+ Code taken from RFC 2617
+
+ Currently we don't support the full SASL authentication mechanism (which includes cyphers)
+*/
+
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+static QByteArray digestMd5ResponseHelper(
+ const QByteArray &alg,
+ const QByteArray &userName,
+ const QByteArray &realm,
+ const QByteArray &password,
+ const QByteArray &nonce, /* nonce from server */
+ const QByteArray &nonceCount, /* 8 hex digits */
+ const QByteArray &cNonce, /* client nonce */
+ const QByteArray &qop, /* qop-value: "", "auth", "auth-int" */
+ const QByteArray &method, /* method from the request */
+ const QByteArray &digestUri, /* requested URL */
+ const QByteArray &hEntity /* H(entity body) if qop="auth-int" */
+ )
+{
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ hash.addData(userName);
+ hash.addData(":", 1);
+ hash.addData(realm);
+ hash.addData(":", 1);
+ hash.addData(password);
+ QByteArray ha1 = hash.result();
+ if (alg.toLower() == "md5-sess") {
+ hash.reset();
+ // RFC 2617 contains an error, it was:
+ // hash.addData(ha1);
+ // but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it
+ // must be the following line:
+ hash.addData(ha1.toHex());
+ hash.addData(":", 1);
+ hash.addData(nonce);
+ hash.addData(":", 1);
+ hash.addData(cNonce);
+ ha1 = hash.result();
+ };
+ ha1 = ha1.toHex();
+
+ // calculate H(A2)
+ hash.reset();
+ hash.addData(method);
+ hash.addData(":", 1);
+ hash.addData(digestUri);
+ if (qop.toLower() == "auth-int") {
+ hash.addData(":", 1);
+ hash.addData(hEntity);
+ }
+ QByteArray ha2hex = hash.result().toHex();
+
+ // calculate response
+ hash.reset();
+ hash.addData(ha1);
+ hash.addData(":", 1);
+ hash.addData(nonce);
+ hash.addData(":", 1);
+ if (!qop.isNull()) {
+ hash.addData(nonceCount);
+ hash.addData(":", 1);
+ hash.addData(cNonce);
+ hash.addData(":", 1);
+ hash.addData(qop);
+ hash.addData(":", 1);
+ }
+ hash.addData(ha2hex);
+ return hash.result().toHex();
+}
+
+QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path)
+{
+ QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
+
+ ++nonceCount;
+ QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
+ while (nonceCountString.length() < 8)
+ nonceCountString.prepend('0');
+
+ QByteArray nonce = options.value("nonce");
+ QByteArray opaque = options.value("opaque");
+ QByteArray qop = options.value("qop");
+
+// qDebug() << "calculating digest: method=" << method << "path=" << path;
+ QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(),
+ realm.toLatin1(), password.toLatin1(),
+ nonce, nonceCountString,
+ cnonce, qop, method,
+ path, QByteArray());
+
+
+ QByteArray credentials;
+ credentials += "username=\"" + user.toLatin1() + "\", ";
+ credentials += "realm=\"" + realm.toLatin1() + "\", ";
+ credentials += "nonce=\"" + nonce + "\", ";
+ credentials += "uri=\"" + path + "\", ";
+ if (!opaque.isEmpty())
+ credentials += "opaque=\"" + opaque + "\", ";
+ credentials += "response=\"" + response + '\"';
+ if (!options.value("algorithm").isEmpty())
+ credentials += ", algorithm=" + options.value("algorithm");
+ if (!options.value("qop").isEmpty()) {
+ credentials += ", qop=" + qop + ", ";
+ credentials += "nc=" + nonceCountString + ", ";
+ credentials += "cnonce=\"" + cnonce + '\"';
+ }
+
+ return credentials;
+}
+
+// ---------------------------- Digest Md5 code ----------------------------------------
+
+
+
+/*
+ * NTLM message flags.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This software is released under the MIT license.
+ */
+
+/*
+ * Indicates that Unicode strings are supported for use in security
+ * buffer data.
+ */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
+
+/*
+ * Indicates that OEM strings are supported for use in security buffer data.
+ */
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002
+
+/*
+ * Requests that the server's authentication realm be included in the
+ * Type 2 message.
+ */
+#define NTLMSSP_REQUEST_TARGET 0x00000004
+
+/*
+ * Specifies that authenticated communication between the client and server
+ * should carry a digital signature (message integrity).
+ */
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
+
+/*
+ * Specifies that authenticated communication between the client and server
+ * should be encrypted (message confidentiality).
+ */
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
+
+/*
+ * Indicates that datagram authentication is being used.
+ */
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
+
+/*
+ * Indicates that the LAN Manager session key should be
+ * used for signing and sealing authenticated communications.
+ */
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
+
+/*
+ * Indicates that NTLM authentication is being used.
+ */
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
+
+/*
+ * Sent by the client in the Type 1 message to indicate that the name of the
+ * domain in which the client workstation has membership is included in the
+ * message. This is used by the server to determine whether the client is
+ * eligible for local authentication.
+ */
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
+
+/*
+ * Sent by the client in the Type 1 message to indicate that the client
+ * workstation's name is included in the message. This is used by the server
+ * to determine whether the client is eligible for local authentication.
+ */
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
+
+/*
+ * Sent by the server to indicate that the server and client are on the same
+ * machine. Implies that the client may use the established local credentials
+ * for authentication instead of calculating a response to the challenge.
+ */
+#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
+
+/*
+ * Indicates that authenticated communication between the client and server
+ * should be signed with a "dummy" signature.
+ */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a domain.
+ */
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a server.
+ */
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a share. Presumably, this is for share-level
+ * authentication. Usage is unclear.
+ */
+#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
+
+/*
+ * Indicates that the NTLM2 signing and sealing scheme should be used for
+ * protecting authenticated communications. Note that this refers to a
+ * particular session security scheme, and is not related to the use of
+ * NTLMv2 authentication.
+ */
+#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that it is including
+ * a Target Information block in the message. The Target Information block
+ * is used in the calculation of the NTLMv2 response.
+ */
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
+
+/*
+ * Indicates that 128-bit encryption is supported.
+ */
+#define NTLMSSP_NEGOTIATE_128 0x20000000
+
+/*
+ * Indicates that the client will provide an encrypted master session key in
+ * the "Session Key" field of the Type 3 message. This is used in signing and
+ * sealing, and is RC4-encrypted using the previous session key as the
+ * encryption key.
+ */
+#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
+
+/*
+ * Indicates that 56-bit encryption is supported.
+ */
+#define NTLMSSP_NEGOTIATE_56 0x80000000
+
+/*
+ * AvId values
+ */
+#define AVTIMESTAMP 7
+
+//#define NTLMV1_CLIENT
+
+
+//************************Global variables***************************
+
+const int blockSize = 64; //As per RFC2104 Block-size is 512 bits
+const int nDigestLen = 16; //Trunctaion Length of the Hmac-Md5 digest
+const quint8 respversion = 1;
+const quint8 hirespversion = 1;
+
+/* usage:
+ // fill up ctx with what we know.
+ QByteArray response = qNtlmPhase1(ctx);
+ // send response (b64 encoded??)
+ // get response from server (b64 decode?)
+ Phase2Block pb;
+ qNtlmDecodePhase2(response, pb);
+ response = qNtlmPhase3(ctx, pb);
+ // send response (b64 encoded??)
+*/
+
+/*
+ TODO:
+ - Fix unicode handling
+ - add v2 handling
+*/
+
+class QNtlmBuffer {
+public:
+ QNtlmBuffer() : len(0), maxLen(0), offset(0) {}
+ quint16 len;
+ quint16 maxLen;
+ quint32 offset;
+ enum { Size = 8 };
+};
+
+class QNtlmPhase1BlockBase
+{
+public:
+ char magic[8];
+ quint32 type;
+ quint32 flags;
+ QNtlmBuffer domain;
+ QNtlmBuffer workstation;
+ enum { Size = 32 };
+};
+
+// ################# check paddings
+class QNtlmPhase2BlockBase
+{
+public:
+ char magic[8];
+ quint32 type;
+ QNtlmBuffer targetName;
+ quint32 flags;
+ unsigned char challenge[8];
+ quint32 context[2];
+ QNtlmBuffer targetInfo;
+ enum { Size = 48 };
+};
+
+class QNtlmPhase3BlockBase {
+public:
+ char magic[8];
+ quint32 type;
+ QNtlmBuffer lmResponse;
+ QNtlmBuffer ntlmResponse;
+ QNtlmBuffer domain;
+ QNtlmBuffer user;
+ QNtlmBuffer workstation;
+ QNtlmBuffer sessionKey;
+ quint32 flags;
+ enum { Size = 64 };
+};
+
+static void qStreamNtlmBuffer(QDataStream& ds, const QByteArray& s)
+{
+ ds.writeRawData(s.constData(), s.size());
+}
+
+
+static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode)
+{
+ if (!unicode) {
+ qStreamNtlmBuffer(ds, s.toLatin1());
+ return;
+ }
+ const ushort *d = s.utf16();
+ for (int i = 0; i < s.length(); ++i)
+ ds << d[i];
+}
+
+
+
+static int qEncodeNtlmBuffer(QNtlmBuffer& buf, int offset, const QByteArray& s)
+{
+ buf.len = s.size();
+ buf.maxLen = buf.len;
+ buf.offset = (offset + 1) & ~1;
+ return buf.offset + buf.len;
+}
+
+
+static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode)
+{
+ if (!unicode)
+ return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
+ buf.len = 2 * s.length();
+ buf.maxLen = buf.len;
+ buf.offset = (offset + 1) & ~1;
+ return buf.offset + buf.len;
+}
+
+
+static QDataStream& operator<<(QDataStream& s, const QNtlmBuffer& b)
+{
+ s << b.len << b.maxLen << b.offset;
+ return s;
+}
+
+static QDataStream& operator>>(QDataStream& s, QNtlmBuffer& b)
+{
+ s >> b.len >> b.maxLen >> b.offset;
+ return s;
+}
+
+
+class QNtlmPhase1Block : public QNtlmPhase1BlockBase
+{ // request
+public:
+ QNtlmPhase1Block() {
+ qstrncpy(magic, "NTLMSSP", 8);
+ type = 1;
+ flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET;
+ }
+
+ // extracted
+ QString domainStr, workstationStr;
+};
+
+
+class QNtlmPhase2Block : public QNtlmPhase2BlockBase
+{ // challenge
+public:
+ QNtlmPhase2Block() {
+ magic[0] = 0;
+ type = 0xffffffff;
+ }
+
+ // extracted
+ QString targetNameStr, targetInfoStr;
+ QByteArray targetInfoBuff;
+};
+
+
+
+class QNtlmPhase3Block : public QNtlmPhase3BlockBase { // response
+public:
+ QNtlmPhase3Block() {
+ qstrncpy(magic, "NTLMSSP", 8);
+ type = 3;
+ flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO;
+ }
+
+ // extracted
+ QByteArray lmResponseBuf, ntlmResponseBuf;
+ QString domainStr, userStr, workstationStr, sessionKeyStr;
+ QByteArray v2Hash;
+};
+
+
+static QDataStream& operator<<(QDataStream& s, const QNtlmPhase1Block& b) {
+ bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
+
+ s.writeRawData(b.magic, sizeof(b.magic));
+ s << b.type;
+ s << b.flags;
+ s << b.domain;
+ s << b.workstation;
+ if (!b.domainStr.isEmpty())
+ qStreamNtlmString(s, b.domainStr, unicode);
+ if (!b.workstationStr.isEmpty())
+ qStreamNtlmString(s, b.workstationStr, unicode);
+ return s;
+}
+
+
+static QDataStream& operator<<(QDataStream& s, const QNtlmPhase3Block& b) {
+ bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
+ s.writeRawData(b.magic, sizeof(b.magic));
+ s << b.type;
+ s << b.lmResponse;
+ s << b.ntlmResponse;
+ s << b.domain;
+ s << b.user;
+ s << b.workstation;
+ s << b.sessionKey;
+ s << b.flags;
+
+ if (!b.domainStr.isEmpty())
+ qStreamNtlmString(s, b.domainStr, unicode);
+
+ qStreamNtlmString(s, b.userStr, unicode);
+
+ if (!b.workstationStr.isEmpty())
+ qStreamNtlmString(s, b.workstationStr, unicode);
+
+ // Send auth info
+ qStreamNtlmBuffer(s, b.lmResponseBuf);
+ qStreamNtlmBuffer(s, b.ntlmResponseBuf);
+
+
+ return s;
+}
+
+
+static QByteArray qNtlmPhase1()
+{
+ QByteArray rc;
+ QDataStream ds(&rc, QIODevice::WriteOnly);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ QNtlmPhase1Block pb;
+ ds << pb;
+ return rc;
+}
+
+
+static QByteArray qStringAsUcs2Le(const QString& src)
+{
+ QByteArray rc(2*src.length(), 0);
+ const unsigned short *s = src.utf16();
+ unsigned short *d = (unsigned short*)rc.data();
+ for (int i = 0; i < src.length(); ++i) {
+ d[i] = qToLittleEndian(s[i]);
+ }
+ return rc;
+}
+
+
+static QString qStringFromUcs2Le(const QByteArray& src)
+{
+ Q_ASSERT(src.size() % 2 == 0);
+ unsigned short *d = (unsigned short*)src.data();
+ for (int i = 0; i < src.length() / 2; ++i) {
+ d[i] = qFromLittleEndian(d[i]);
+ }
+ return QString((const QChar *)src.data(), src.size()/2);
+}
+
+#ifdef NTLMV1_CLIENT
+static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch)
+{
+ QCryptographicHash md4(QCryptographicHash::Md4);
+ QByteArray asUcs2Le = qStringAsUcs2Le(ctx->password);
+ md4.addData(asUcs2Le.data(), asUcs2Le.size());
+
+ unsigned char md4hash[22];
+ memset(md4hash, 0, sizeof(md4hash));
+ QByteArray hash = md4.result();
+ Q_ASSERT(hash.size() == 16);
+ memcpy(md4hash, hash.constData(), 16);
+
+ QByteArray rc(24, 0);
+ deshash((unsigned char *)rc.data(), md4hash, (unsigned char *)ch.challenge);
+ deshash((unsigned char *)rc.data() + 8, md4hash + 7, (unsigned char *)ch.challenge);
+ deshash((unsigned char *)rc.data() + 16, md4hash + 14, (unsigned char *)ch.challenge);
+
+ hash.fill(0);
+ return rc;
+}
+
+
+static QByteArray qEncodeLmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch)
+{
+ QByteArray hash(21, 0);
+ QByteArray key(14, 0);
+ qstrncpy(key.data(), ctx->password.toUpper().toLatin1(), 14);
+ const char *block = "KGS!@#$%";
+
+ deshash((unsigned char *)hash.data(), (unsigned char *)key.data(), (unsigned char *)block);
+ deshash((unsigned char *)hash.data() + 8, (unsigned char *)key.data() + 7, (unsigned char *)block);
+ key.fill(0);
+
+ QByteArray rc(24, 0);
+ deshash((unsigned char *)rc.data(), (unsigned char *)hash.data(), ch.challenge);
+ deshash((unsigned char *)rc.data() + 8, (unsigned char *)hash.data() + 7, ch.challenge);
+ deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge);
+
+ hash.fill(0);
+ return rc;
+}
+#endif
+
+/*********************************************************************
+* Function Name: qEncodeHmacMd5
+* Params:
+* key: Type - QByteArray
+* - It is the Authentication key
+* message: Type - QByteArray
+* - This is the actual message which will be encoded
+* using HMacMd5 hash algorithm
+*
+* Return Value:
+* hmacDigest: Type - QByteArray
+*
+* Description:
+* This function will be used to encode the input message using
+* HMacMd5 hash algorithm.
+*
+* As per the RFC2104 the HMacMd5 algorithm can be specified
+* ---------------------------------------
+* MD5(K XOR opad, MD5(K XOR ipad, text))
+* ---------------------------------------
+*
+*********************************************************************/
+QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message)
+{
+ Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check");
+ Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check");
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ QByteArray hMsg;
+
+ QByteArray iKeyPad(blockSize, 0x36);
+ QByteArray oKeyPad(blockSize, 0x5c);
+
+ hash.reset();
+ // Adjust the key length to blockSize
+
+ if(blockSize < key.length()) {
+ hash.addData(key);
+ key = hash.result(); //MD5 will always return 16 bytes length output
+ }
+
+ //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms)
+ //key size can be max of Block size only
+ key = key.leftJustified(blockSize,0,true);
+
+ //iKeyPad, oKeyPad and key are all of same size "blockSize"
+
+ //xor of iKeyPad with Key and store the result into iKeyPad
+ for(int i = 0; i<key.size();i++) {
+ iKeyPad[i] = key[i]^iKeyPad[i];
+ }
+
+ //xor of oKeyPad with Key and store the result into oKeyPad
+ for(int i = 0; i<key.size();i++) {
+ oKeyPad[i] = key[i]^oKeyPad[i];
+ }
+
+ iKeyPad.append(message); // (K0 xor ipad) || text
+
+ hash.reset();
+ hash.addData(iKeyPad);
+ hMsg = hash.result();
+ //Digest gen after pass-1: H((K0 xor ipad)||text)
+
+ QByteArray hmacDigest;
+ oKeyPad.append(hMsg);
+ hash.reset();
+ hash.addData(oKeyPad);
+ hmacDigest = hash.result();
+ // H((K0 xor opad )|| H((K0 xor ipad) || text))
+
+ /*hmacDigest should not be less than half the length of the HMAC output
+ (to match the birthday attack bound) and not less than 80 bits
+ (a suitable lower bound on the number of bits that need to be
+ predicted by an attacker).
+ Refer RFC 2104 for more details on truncation part */
+
+ /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec
+ (RFC 2104) also says digest length should be 16 bytes*/
+ return hmacDigest;
+}
+
+static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx,
+ QNtlmPhase3Block *phase3)
+{
+ Q_ASSERT(phase3 != 0);
+ // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated
+ // only once and stored and reused
+ if(phase3->v2Hash.size() == 0) {
+ QCryptographicHash md4(QCryptographicHash::Md4);
+ QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
+ md4.addData(passUnicode.data(), passUnicode.size());
+
+ QByteArray hashKey = md4.result();
+ Q_ASSERT(hashKey.size() == 16);
+ // Assuming the user and domain is always unicode in challenge
+ QByteArray message =
+ qStringAsUcs2Le(ctx->extractedUser.toUpper()) +
+ qStringAsUcs2Le(phase3->domainStr);
+
+ phase3->v2Hash = qEncodeHmacMd5(hashKey, message);
+ }
+ return phase3->v2Hash;
+}
+
+static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx)
+{
+ Q_ASSERT(ctx->cnonce.size() >= 8);
+ QByteArray clientCh = ctx->cnonce.right(8);
+ return clientCh;
+}
+
+// caller has to ensure a valid targetInfoBuff
+static QByteArray qExtractServerTime(const QByteArray& targetInfoBuff)
+{
+ QByteArray timeArray;
+ QDataStream ds(targetInfoBuff);
+ ds.setByteOrder(QDataStream::LittleEndian);
+
+ quint16 avId;
+ quint16 avLen;
+
+ ds >> avId;
+ ds >> avLen;
+ while(avId != 0) {
+ if(avId == AVTIMESTAMP) {
+ timeArray.resize(avLen);
+ //avLen size of QByteArray is allocated
+ ds.readRawData(timeArray.data(), avLen);
+ break;
+ }
+ ds.skipRawData(avLen);
+ ds >> avId;
+ ds >> avLen;
+ }
+ return timeArray;
+}
+
+static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx,
+ const QNtlmPhase2Block& ch,
+ QNtlmPhase3Block *phase3)
+{
+ Q_ASSERT(phase3 != 0);
+ // return value stored in phase3
+ qCreatev2Hash(ctx, phase3);
+
+ QByteArray temp;
+ QDataStream ds(&temp, QIODevice::WriteOnly);
+ ds.setByteOrder(QDataStream::LittleEndian);
+
+ ds << respversion;
+ ds << hirespversion;
+
+ //Reserved
+ QByteArray reserved1(6, 0);
+ ds.writeRawData(reserved1.constData(), reserved1.size());
+
+ quint64 time = 0;
+ QByteArray timeArray;
+
+ if(ch.targetInfo.len)
+ {
+ timeArray = qExtractServerTime(ch.targetInfoBuff);
+ }
+
+ //if server sends time, use it instead of current time
+ if(timeArray.size()) {
+ ds.writeRawData(timeArray.constData(), timeArray.size());
+ } else {
+ QDateTime currentTime(QDate::currentDate(),
+ QTime::currentTime(), Qt::UTC);
+
+ // number of seconds between 1601 and epoc(1970)
+ // 369 years, 89 leap years
+ // ((369 * 365) + 89) * 24 * 3600 = 11644473600
+
+ time = Q_UINT64_C(currentTime.toTime_t() + 11644473600);
+
+ // represented as 100 nano seconds
+ time = Q_UINT64_C(time * 10000000);
+ ds << time;
+ }
+
+ //8 byte client challenge
+ QByteArray clientCh = clientChallenge(ctx);
+ ds.writeRawData(clientCh.constData(), clientCh.size());
+
+ //Reserved
+ QByteArray reserved2(4, 0);
+ ds.writeRawData(reserved2.constData(), reserved2.size());
+
+ if (ch.targetInfo.len > 0) {
+ ds.writeRawData(ch.targetInfoBuff.constData(),
+ ch.targetInfoBuff.size());
+ }
+
+ //Reserved
+ QByteArray reserved3(4, 0);
+ ds.writeRawData(reserved3.constData(), reserved3.size());
+
+ QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
+ message.append(temp);
+
+ QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
+ ntChallengeResp.append(temp);
+
+ return ntChallengeResp;
+}
+
+static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx,
+ const QNtlmPhase2Block& ch,
+ QNtlmPhase3Block *phase3)
+{
+ Q_ASSERT(phase3 != 0);
+ // return value stored in phase3
+ qCreatev2Hash(ctx, phase3);
+
+ QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
+ QByteArray clientCh = clientChallenge(ctx);
+
+ message.append(clientCh);
+
+ QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
+ lmChallengeResp.append(clientCh);
+
+ return lmChallengeResp;
+}
+
+static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch)
+{
+ Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase));
+ if (data.size() < QNtlmPhase2BlockBase::Size)
+ return false;
+
+
+ QDataStream ds(data);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ if (ds.readRawData(ch.magic, 8) < 8)
+ return false;
+ if (strncmp(ch.magic, "NTLMSSP", 8) != 0)
+ return false;
+
+ ds >> ch.type;
+ if (ch.type != 2)
+ return false;
+
+ ds >> ch.targetName;
+ ds >> ch.flags;
+ if (ds.readRawData((char *)ch.challenge, 8) < 8)
+ return false;
+ ds >> ch.context[0] >> ch.context[1];
+ ds >> ch.targetInfo;
+
+ if (ch.targetName.len > 0) {
+ if (ch.targetName.len + ch.targetName.offset >= (unsigned)data.size())
+ return false;
+
+ ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
+ }
+
+ if (ch.targetInfo.len > 0) {
+ if (ch.targetInfo.len + ch.targetInfo.offset > (unsigned)data.size())
+ return false;
+
+ ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len);
+ }
+
+ return true;
+}
+
+
+static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data)
+{
+ QNtlmPhase2Block ch;
+ if (!qNtlmDecodePhase2(phase2data, ch))
+ return QByteArray();
+
+ QByteArray rc;
+ QDataStream ds(&rc, QIODevice::WriteOnly);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ QNtlmPhase3Block pb;
+
+ bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE;
+
+ pb.flags = NTLMSSP_NEGOTIATE_NTLM;
+ if (unicode)
+ pb.flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ else
+ pb.flags |= NTLMSSP_NEGOTIATE_OEM;
+
+
+ int offset = QNtlmPhase3BlockBase::Size;
+ Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase));
+
+ if(ctx->userDomain.isEmpty()) {
+ offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
+ pb.domainStr = ch.targetNameStr;
+ } else {
+ offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode);
+ pb.domainStr = ctx->userDomain;
+ }
+
+ offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode);
+ pb.userStr = ctx->extractedUser;
+
+ offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
+ pb.workstationStr = ctx->workstation;
+
+ // Get LM response
+#ifdef NTLMV1_CLIENT
+ pb.lmResponseBuf = qEncodeLmResponse(ctx, ch);
+#else
+ if (ch.targetInfo.len > 0) {
+ pb.lmResponseBuf = QByteArray();
+ } else {
+ pb.lmResponseBuf = qEncodeLmv2Response(ctx, ch, &pb);
+ }
+#endif
+ offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf);
+
+ // Get NTLM response
+#ifdef NTLMV1_CLIENT
+ pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch);
+#else
+ pb.ntlmResponseBuf = qEncodeNtlmv2Response(ctx, ch, &pb);
+#endif
+ offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf);
+
+
+ // Encode and send
+ ds << pb;
+
+ return rc;
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h
new file mode 100644
index 0000000000..b97802ad3e
--- /dev/null
+++ b/src/network/kernel/qauthenticator.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAUTHENTICATOR_H
+#define QAUTHENTICATOR_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QAuthenticatorPrivate;
+class QUrl;
+
+class Q_NETWORK_EXPORT QAuthenticator
+{
+public:
+ QAuthenticator();
+ ~QAuthenticator();
+
+ QAuthenticator(const QAuthenticator &other);
+ QAuthenticator &operator=(const QAuthenticator &other);
+
+ bool operator==(const QAuthenticator &other) const;
+ inline bool operator!=(const QAuthenticator &other) const { return !operator==(other); }
+
+ QString user() const;
+ void setUser(const QString &user);
+
+ QString password() const;
+ void setPassword(const QString &password);
+
+ QString realm() const;
+
+ QVariant option(const QString &opt) const;
+ QVariantHash options() const;
+ void setOption(const QString &opt, const QVariant &value);
+
+ bool isNull() const;
+ void detach();
+private:
+ friend class QAuthenticatorPrivate;
+ QAuthenticatorPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
new file mode 100644
index 0000000000..7db2dedc7e
--- /dev/null
+++ b/src/network/kernel/qauthenticator_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAUTHENTICATOR_P_H
+#define QAUTHENTICATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qhash.h>
+#include <qbytearray.h>
+#include <qstring.h>
+#include <qauthenticator.h>
+#include <qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpResponseHeader;
+
+class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
+{
+public:
+ enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 };
+ QAuthenticatorPrivate();
+
+ QAtomicInt ref;
+ QString user;
+ QString extractedUser;
+ QString password;
+ QVariantHash options;
+ Method method;
+ QString realm;
+ QByteArray challenge;
+
+ enum Phase {
+ Start,
+ Phase2,
+ Done,
+ Invalid
+ };
+ Phase phase;
+
+ // digest specific
+ QByteArray cnonce;
+ int nonceCount;
+
+ // ntlm specific
+ QString workstation;
+ QString userDomain;
+
+ QByteArray calculateResponse(const QByteArray &method, const QByteArray &path);
+
+ inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; }
+ inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
+
+ QByteArray digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path);
+ static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(const QByteArray &challenge);
+
+#ifndef QT_NO_HTTP
+ void parseHttpResponse(const QHttpResponseHeader &, bool isProxy);
+#endif
+ void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy);
+
+};
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
new file mode 100644
index 0000000000..ae7d7a1486
--- /dev/null
+++ b/src/network/kernel/qhostaddress.cpp
@@ -0,0 +1,1170 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhostaddress.h"
+#include "qhostaddress_p.h"
+#include "qdebug.h"
+#include "qplatformdefs.h"
+#include "qstringlist.h"
+#include "qendian.h"
+#ifndef QT_NO_DATASTREAM
+#include <qdatastream.h>
+#endif
+#if defined(Q_OS_WINCE)
+#include <winsock.h>
+#endif
+
+#ifdef QT_LINUXBASE
+# include <arpa/inet.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#define QT_ENSURE_PARSED(a) \
+ do { \
+ if (!(a)->d->isParsed) \
+ (a)->d->parse(); \
+ } while (0)
+
+#ifdef Q_OS_WIN
+# if !defined (QT_NO_IPV6)
+// sockaddr_in6 size changed between old and new SDK
+// Only the new version is the correct one, so always
+// use this structure.
+#if defined(Q_OS_WINCE)
+# if !defined(u_char)
+# define u_char unsigned char
+# endif
+# if !defined(u_short)
+# define u_short unsigned short
+# endif
+# if !defined(u_long)
+# define u_long unsigned long
+# endif
+#endif
+struct qt_in6_addr {
+ u_char qt_s6_addr[16];
+};
+typedef struct {
+ short sin6_family; /* AF_INET6 */
+ u_short sin6_port; /* Transport level port number */
+ u_long sin6_flowinfo; /* IPv6 flow information */
+ struct qt_in6_addr sin6_addr; /* IPv6 address */
+ u_long sin6_scope_id; /* set of interfaces for a scope */
+} qt_sockaddr_in6;
+# else
+typedef void * qt_sockaddr_in6 ;
+# endif
+# ifndef AF_INET6
+# define AF_INET6 23 /* Internetwork Version 6 */
+# endif
+#else
+#define qt_sockaddr_in6 sockaddr_in6
+#define qt_s6_addr s6_addr
+#endif
+
+
+class QHostAddressPrivate
+{
+public:
+ QHostAddressPrivate();
+
+ void setAddress(quint32 a_ = 0);
+ void setAddress(const quint8 *a_);
+ void setAddress(const Q_IPV6ADDR &a_);
+
+ bool parse();
+ void clear();
+
+ quint32 a; // IPv4 address
+ Q_IPV6ADDR a6; // IPv6 address
+ QAbstractSocket::NetworkLayerProtocol protocol;
+
+ QString ipString;
+ bool isParsed;
+ QString scopeId;
+
+ friend class QHostAddress;
+};
+
+QHostAddressPrivate::QHostAddressPrivate()
+ : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol), isParsed(true)
+{
+ memset(&a6, 0, sizeof(a6));
+}
+
+void QHostAddressPrivate::setAddress(quint32 a_)
+{
+ a = a_;
+ protocol = QAbstractSocket::IPv4Protocol;
+ isParsed = true;
+}
+
+void QHostAddressPrivate::setAddress(const quint8 *a_)
+{
+ for (int i = 0; i < 16; i++)
+ a6[i] = a_[i];
+ protocol = QAbstractSocket::IPv6Protocol;
+ isParsed = true;
+}
+
+void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_)
+{
+ a6 = a_;
+ a = 0;
+ protocol = QAbstractSocket::IPv6Protocol;
+ isParsed = true;
+}
+
+static bool parseIp4(const QString& address, quint32 *addr)
+{
+ QStringList ipv4 = address.split(QLatin1String("."));
+ if (ipv4.count() != 4)
+ return false;
+
+ quint32 ipv4Address = 0;
+ for (int i = 0; i < 4; ++i) {
+ bool ok = false;
+ uint byteValue = ipv4.at(i).toUInt(&ok);
+ if (!ok || byteValue > 255)
+ return false;
+
+ ipv4Address <<= 8;
+ ipv4Address += byteValue;
+ }
+
+ *addr = ipv4Address;
+ return true;
+}
+
+static bool parseIp6(const QString &address, quint8 *addr, QString *scopeId)
+{
+ QString tmp = address;
+ int scopeIdPos = tmp.lastIndexOf(QLatin1Char('%'));
+ if (scopeIdPos != -1) {
+ *scopeId = tmp.mid(scopeIdPos + 1);
+ tmp.chop(tmp.size() - scopeIdPos);
+ } else {
+ scopeId->clear();
+ }
+
+ QStringList ipv6 = tmp.split(QLatin1String(":"));
+ int count = ipv6.count();
+ if (count < 3 || count > 8)
+ return false;
+
+ int colonColon = tmp.count(QLatin1String("::"));
+ if(count == 8 && colonColon > 1)
+ return false;
+
+ // address can be compressed with a "::", but that
+ // may only appear once (see RFC 1884)
+ // the statement below means:
+ // if(shortened notation is not used AND
+ // ((pure IPv6 notation AND less than 8 parts) OR
+ // ((mixed IPv4/6 notation AND less than 7 parts)))
+ if(colonColon != 1 && count < (tmp.contains(QLatin1Char('.')) ? 7 : 8))
+ return false;
+
+ int mc = 16;
+ int fillCount = 9 - count; // number of 0 words to fill in the middle
+ for (int i = count - 1; i >= 0; --i) {
+ if (mc <= 0)
+ return false;
+
+ if (ipv6.at(i).isEmpty()) {
+ if (i == count - 1) {
+ // special case: ":" is last character
+ if (!ipv6.at(i - 1).isEmpty())
+ return false;
+ addr[--mc] = 0;
+ addr[--mc] = 0;
+ } else if (i == 0) {
+ // special case: ":" is first character
+ if (!ipv6.at(i + 1).isEmpty())
+ return false;
+ addr[--mc] = 0;
+ addr[--mc] = 0;
+ } else {
+ for (int j = 0; j < fillCount; ++j) {
+ if (mc <= 0)
+ return false;
+ addr[--mc] = 0;
+ addr[--mc] = 0;
+ }
+ }
+ } else {
+ bool ok = false;
+ uint byteValue = ipv6.at(i).toUInt(&ok, 16);
+ if (ok && byteValue <= 0xffff) {
+ addr[--mc] = byteValue & 0xff;
+ addr[--mc] = (byteValue >> 8) & 0xff;
+ } else {
+ if (i != count - 1)
+ return false;
+
+ // parse the ipv4 part of a mixed type
+ quint32 maybeIp4;
+ if (!parseIp4(ipv6.at(i), &maybeIp4))
+ return false;
+
+ addr[--mc] = maybeIp4 & 0xff;
+ addr[--mc] = (maybeIp4 >> 8) & 0xff;
+ addr[--mc] = (maybeIp4 >> 16) & 0xff;
+ addr[--mc] = (maybeIp4 >> 24) & 0xff;
+ --fillCount;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool QHostAddressPrivate::parse()
+{
+ isParsed = true;
+ protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ QString a = ipString.simplified();
+
+ // All IPv6 addresses contain a ':', and may contain a '.'.
+ if (a.contains(QLatin1Char(':'))) {
+ quint8 maybeIp6[16];
+ if (parseIp6(a, maybeIp6, &scopeId)) {
+ setAddress(maybeIp6);
+ protocol = QAbstractSocket::IPv6Protocol;
+ return true;
+ }
+ }
+
+ // All IPv4 addresses contain a '.'.
+ if (a.contains(QLatin1Char('.'))) {
+ quint32 maybeIp4 = 0;
+ if (parseIp4(a, &maybeIp4)) {
+ setAddress(maybeIp4);
+ protocol = QAbstractSocket::IPv4Protocol;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QHostAddressPrivate::clear()
+{
+ a = 0;
+ protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ isParsed = true;
+ memset(&a6, 0, sizeof(a6));
+}
+
+
+bool QNetmaskAddress::setAddress(const QString &address)
+{
+ length = -1;
+ QHostAddress other;
+ return other.setAddress(address) && setAddress(other);
+}
+
+bool QNetmaskAddress::setAddress(const QHostAddress &address)
+{
+ static const quint8 zeroes[16] = { 0 };
+ union {
+ quint32 v4;
+ quint8 v6[16];
+ } ip;
+
+ int netmask = 0;
+ quint8 *ptr = ip.v6;
+ quint8 *end;
+ length = -1;
+
+ QHostAddress::operator=(address);
+
+ if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ ip.v4 = qToBigEndian(d->a);
+ end = ptr + 4;
+ } else if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ memcpy(ip.v6, d->a6.c, 16);
+ end = ptr + 16;
+ } else {
+ d->clear();
+ return false;
+ }
+
+ while (ptr < end) {
+ switch (*ptr) {
+ case 255:
+ netmask += 8;
+ ++ptr;
+ continue;
+
+ default:
+ d->clear();
+ return false; // invalid IP-style netmask
+
+ // the rest always falls through
+ case 254:
+ ++netmask;
+ case 252:
+ ++netmask;
+ case 248:
+ ++netmask;
+ case 240:
+ ++netmask;
+ case 224:
+ ++netmask;
+ case 192:
+ ++netmask;
+ case 128:
+ ++netmask;
+ case 0:
+ break;
+ }
+ break;
+ }
+
+ // confirm that the rest is only zeroes
+ if (ptr < end && memcmp(ptr + 1, zeroes, end - ptr - 1) != 0) {
+ d->clear();
+ return false;
+ }
+
+ length = netmask;
+ return true;
+}
+
+static void clearBits(quint8 *where, int start, int end)
+{
+ Q_ASSERT(end == 32 || end == 128);
+ if (start == end)
+ return;
+
+ // for the byte where 'start' is, clear the lower bits only
+ quint8 bytemask = 256 - (1 << (8 - (start & 7)));
+ where[start / 8] &= bytemask;
+
+ // for the tail part, clear everything
+ memset(where + (start + 7) / 8, 0, end / 8 - (start + 7) / 8);
+}
+
+int QNetmaskAddress::prefixLength() const
+{
+ return length;
+}
+
+void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int newLength)
+{
+ length = newLength;
+ if (length < 0 || length > (proto == QAbstractSocket::IPv4Protocol ? 32 :
+ proto == QAbstractSocket::IPv6Protocol ? 128 : -1)) {
+ // invalid information, reject
+ d->protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ length = -1;
+ return;
+ }
+
+ d->protocol = proto;
+ if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ if (length == 0) {
+ d->a = 0;
+ } else if (length == 32) {
+ d->a = quint32(0xffffffff);
+ } else {
+ d->a = quint32(0xffffffff) >> (32 - length) << (32 - length);
+ }
+ } else {
+ memset(d->a6.c, 0xFF, sizeof(d->a6));
+ clearBits(d->a6.c, length, 128);
+ }
+}
+
+/*!
+ \class QHostAddress
+ \brief The QHostAddress class provides an IP address.
+ \ingroup network
+ \inmodule QtNetwork
+
+ This class holds an IPv4 or IPv6 address in a platform- and
+ protocol-independent manner.
+
+ QHostAddress is normally used with the QTcpSocket, QTcpServer,
+ and QUdpSocket to connect to a host or to set up a server.
+
+ A host address is set with setAddress(), and retrieved with
+ toIPv4Address(), toIPv6Address(), or toString(). You can check the
+ type with protocol().
+
+ \note Please note that QHostAddress does not do DNS lookups.
+ QHostInfo is needed for that.
+
+ The class also supports common predefined addresses: \l Null, \l
+ LocalHost, \l LocalHostIPv6, \l Broadcast, and \l Any.
+
+ \sa QHostInfo, QTcpSocket, QTcpServer, QUdpSocket
+*/
+
+/*! \enum QHostAddress::SpecialAddress
+
+ \value Null The null address object. Equivalent to QHostAddress().
+ \value LocalHost The IPv4 localhost address. Equivalent to QHostAddress("127.0.0.1").
+ \value LocalHostIPv6 The IPv6 localhost address. Equivalent to QHostAddress("::1").
+ \value Broadcast The IPv4 broadcast address. Equivalent to QHostAddress("255.255.255.255").
+ \value Any The IPv4 any-address. Equivalent to QHostAddress("0.0.0.0").
+ \value AnyIPv6 The IPv6 any-address. Equivalent to QHostAddress("::").
+*/
+
+/*! Constructs a host address object with the IP address 0.0.0.0.
+
+ \sa clear()
+*/
+QHostAddress::QHostAddress()
+ : d(new QHostAddressPrivate)
+{
+}
+
+/*!
+ Constructs a host address object with the IPv4 address \a ip4Addr.
+*/
+QHostAddress::QHostAddress(quint32 ip4Addr)
+ : d(new QHostAddressPrivate)
+{
+ setAddress(ip4Addr);
+}
+
+/*!
+ Constructs a host address object with the IPv6 address \a ip6Addr.
+
+ \a ip6Addr must be a 16-byte array in network byte order (big
+ endian).
+*/
+QHostAddress::QHostAddress(quint8 *ip6Addr)
+ : d(new QHostAddressPrivate)
+{
+ setAddress(ip6Addr);
+}
+
+/*!
+ Constructs a host address object with the IPv6 address \a ip6Addr.
+*/
+QHostAddress::QHostAddress(const Q_IPV6ADDR &ip6Addr)
+ : d(new QHostAddressPrivate)
+{
+ setAddress(ip6Addr);
+}
+
+/*!
+ Constructs an IPv4 or IPv6 address based on the string \a address
+ (e.g., "127.0.0.1").
+
+ \sa setAddress()
+*/
+QHostAddress::QHostAddress(const QString &address)
+ : d(new QHostAddressPrivate)
+{
+ d->ipString = address;
+ d->isParsed = false;
+}
+
+/*!
+ \fn QHostAddress::QHostAddress(const sockaddr *sockaddr)
+
+ Constructs an IPv4 or IPv6 address using the address specified by
+ the native structure \a sockaddr.
+
+ \sa setAddress()
+*/
+QHostAddress::QHostAddress(const struct sockaddr *sockaddr)
+ : d(new QHostAddressPrivate)
+{
+ if (sockaddr->sa_family == AF_INET)
+ setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr));
+#ifndef QT_NO_IPV6
+ else if (sockaddr->sa_family == AF_INET6)
+ setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr);
+#endif
+}
+
+/*!
+ Constructs a copy of the given \a address.
+*/
+QHostAddress::QHostAddress(const QHostAddress &address)
+ : d(new QHostAddressPrivate(*address.d.data()))
+{
+}
+
+/*!
+ Constructs a QHostAddress object for \a address.
+*/
+QHostAddress::QHostAddress(SpecialAddress address)
+ : d(new QHostAddressPrivate)
+{
+ switch (address) {
+ case Null:
+ break;
+ case Broadcast:
+ setAddress(QLatin1String("255.255.255.255"));
+ break;
+ case LocalHost:
+ setAddress(QLatin1String("127.0.0.1"));
+ break;
+ case LocalHostIPv6:
+ setAddress(QLatin1String("::1"));
+ break;
+ case Any:
+ setAddress(QLatin1String("0.0.0.0"));
+ break;
+ case AnyIPv6:
+ setAddress(QLatin1String("::"));
+ break;
+ }
+}
+
+/*!
+ Destroys the host address object.
+*/
+QHostAddress::~QHostAddress()
+{
+}
+
+/*!
+ Assigns another host \a address to this object, and returns a reference
+ to this object.
+*/
+QHostAddress &QHostAddress::operator=(const QHostAddress &address)
+{
+ *d.data() = *address.d.data();
+ return *this;
+}
+
+/*!
+ Assigns the host address \a address to this object, and returns a
+ reference to this object.
+
+ \sa setAddress()
+*/
+QHostAddress &QHostAddress::operator=(const QString &address)
+{
+ setAddress(address);
+ return *this;
+}
+
+/*!
+ \fn bool QHostAddress::operator!=(const QHostAddress &other) const
+ \since 4.2
+
+ Returns true if this host address is not the same as the \a other
+ address given; otherwise returns false.
+*/
+
+/*!
+ \fn bool QHostAddress::operator!=(SpecialAddress other) const
+
+ Returns true if this host address is not the same as the \a other
+ address given; otherwise returns false.
+*/
+
+/*!
+ Sets the host address to 0.0.0.0.
+*/
+void QHostAddress::clear()
+{
+ d->clear();
+}
+
+/*!
+ Set the IPv4 address specified by \a ip4Addr.
+*/
+void QHostAddress::setAddress(quint32 ip4Addr)
+{
+ d->setAddress(ip4Addr);
+}
+
+/*!
+ \overload
+
+ Set the IPv6 address specified by \a ip6Addr.
+
+ \a ip6Addr must be an array of 16 bytes in network byte order
+ (high-order byte first).
+*/
+void QHostAddress::setAddress(quint8 *ip6Addr)
+{
+ d->setAddress(ip6Addr);
+}
+
+/*!
+ \overload
+
+ Set the IPv6 address specified by \a ip6Addr.
+*/
+void QHostAddress::setAddress(const Q_IPV6ADDR &ip6Addr)
+{
+ d->setAddress(ip6Addr);
+}
+
+/*!
+ \overload
+
+ Sets the IPv4 or IPv6 address specified by the string
+ representation specified by \a address (e.g. "127.0.0.1").
+ Returns true and sets the address if the address was successfully
+ parsed; otherwise returns false.
+*/
+bool QHostAddress::setAddress(const QString &address)
+{
+ d->ipString = address;
+ return d->parse();
+}
+
+/*!
+ \fn void QHostAddress::setAddress(const sockaddr *sockaddr)
+ \overload
+
+ Sets the IPv4 or IPv6 address specified by the native structure \a
+ sockaddr. Returns true and sets the address if the address was
+ successfully parsed; otherwise returns false.
+*/
+void QHostAddress::setAddress(const struct sockaddr *sockaddr)
+{
+ clear();
+ if (sockaddr->sa_family == AF_INET)
+ setAddress(htonl(((sockaddr_in *)sockaddr)->sin_addr.s_addr));
+#ifndef QT_NO_IPV6
+ else if (sockaddr->sa_family == AF_INET6)
+ setAddress(((qt_sockaddr_in6 *)sockaddr)->sin6_addr.qt_s6_addr);
+#endif
+}
+
+/*!
+ Returns the IPv4 address as a number.
+
+ For example, if the address is 127.0.0.1, the returned value is
+ 2130706433 (i.e. 0x7f000001).
+
+ This value is only valid if the Protocol() is
+ \l{QAbstractSocket::}{IPv4Protocol}.
+
+ \sa toString()
+*/
+quint32 QHostAddress::toIPv4Address() const
+{
+ QT_ENSURE_PARSED(this);
+ return d->a;
+}
+
+/*!
+ Returns the network layer protocol of the host address.
+*/
+QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const
+{
+ QT_ENSURE_PARSED(this);
+ return d->protocol;
+}
+
+/*!
+ Returns the IPv6 address as a Q_IPV6ADDR structure. The structure
+ consists of 16 unsigned characters.
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostaddress.cpp 0
+
+ This value is only valid if the protocol() is
+ \l{QAbstractSocket::}{IPv6Protocol}.
+
+ \sa toString()
+*/
+Q_IPV6ADDR QHostAddress::toIPv6Address() const
+{
+ QT_ENSURE_PARSED(this);
+ return d->a6;
+}
+
+/*!
+ 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".
+
+ \sa toIPv4Address()
+*/
+QString QHostAddress::toString() const
+{
+ QT_ENSURE_PARSED(this);
+ if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ quint32 i = toIPv4Address();
+ QString s;
+ s.sprintf("%d.%d.%d.%d", (i>>24) & 0xff, (i>>16) & 0xff,
+ (i >> 8) & 0xff, i & 0xff);
+ return s;
+ }
+
+ if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ quint16 ugle[8];
+ for (int i = 0; i < 8; i++) {
+ 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]);
+ if (!d->scopeId.isEmpty())
+ s.append(QLatin1Char('%') + d->scopeId);
+ return s;
+ }
+
+ return QString();
+}
+
+/*!
+ \since 4.1
+
+ Returns the scope ID of an IPv6 address. For IPv4 addresses, or if the
+ address does not contain a scope ID, an empty QString is returned.
+
+ The IPv6 scope ID specifies the scope of \e reachability for non-global
+ IPv6 addresses, limiting the area in which the address can be used. All
+ IPv6 addresses are associated with such a reachability scope. The scope ID
+ is used to disambiguate addresses that are not guaranteed to be globally
+ unique.
+
+ IPv6 specifies the following four levels of reachability:
+
+ \list
+
+ \o Node-local: Addresses that are only used for communicating with
+ services on the same interface (e.g., the loopback interface "::1").
+
+ \o Link-local: Addresses that are local to the network interface
+ (\e{link}). There is always one link-local address for each IPv6 interface
+ on your host. Link-local addresses ("fe80...") are generated from the MAC
+ address of the local network adaptor, and are not guaranteed to be unique.
+
+ \o Site-local: Addresses that are local to the site / private network
+ (e.g., the company intranet). Site-local addresses ("fec0...") are
+ usually distributed by the site router, and are not guaranteed to be
+ unique outside of the local site.
+
+ \o Global: For globally routable addresses, such as public servers on the
+ Internet.
+
+ \endlist
+
+ When using a link-local or site-local address for IPv6 connections, you
+ must specify the scope ID. The scope ID for a link-local address is
+ usually the same as the interface name (e.g., "eth0", "en1") or number
+ (e.g., "1", "2").
+
+ \sa setScopeId()
+*/
+QString QHostAddress::scopeId() const
+{
+ QT_ENSURE_PARSED(this);
+ return (d->protocol == QAbstractSocket::IPv6Protocol) ? d->scopeId : QString();
+}
+
+/*!
+ \since 4.1
+
+ Sets the IPv6 scope ID of the address to \a id. If the address
+ protocol is not IPv6, this function does nothing.
+*/
+void QHostAddress::setScopeId(const QString &id)
+{
+ QT_ENSURE_PARSED(this);
+ if (d->protocol == QAbstractSocket::IPv6Protocol)
+ d->scopeId = id;
+}
+
+/*!
+ Returns true if this host address is the same as the \a other address
+ given; otherwise returns false.
+*/
+bool QHostAddress::operator==(const QHostAddress &other) const
+{
+ QT_ENSURE_PARSED(this);
+ QT_ENSURE_PARSED(&other);
+
+ if (d->protocol == QAbstractSocket::IPv4Protocol)
+ return other.d->protocol == QAbstractSocket::IPv4Protocol && d->a == other.d->a;
+ if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ return other.d->protocol == QAbstractSocket::IPv6Protocol
+ && memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
+ }
+ return d->protocol == other.d->protocol;
+}
+
+/*!
+ Returns true if this host address is the same as the \a other
+ address given; otherwise returns false.
+*/
+bool QHostAddress::operator ==(SpecialAddress other) const
+{
+ QT_ENSURE_PARSED(this);
+ QHostAddress otherAddress(other);
+ QT_ENSURE_PARSED(&otherAddress);
+
+ if (d->protocol == QAbstractSocket::IPv4Protocol)
+ return otherAddress.d->protocol == QAbstractSocket::IPv4Protocol && d->a == otherAddress.d->a;
+ if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ return otherAddress.d->protocol == QAbstractSocket::IPv6Protocol
+ && memcmp(&d->a6, &otherAddress.d->a6, sizeof(Q_IPV6ADDR)) == 0;
+ }
+ return int(other) == int(Null);
+}
+
+/*!
+ Returns true if this host address is null (INADDR_ANY or in6addr_any).
+ The default constructor creates a null address, and that address is
+ not valid for any host or interface.
+*/
+bool QHostAddress::isNull() const
+{
+ QT_ENSURE_PARSED(this);
+ return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol;
+}
+
+/*!
+ \fn quint32 QHostAddress::ip4Addr() const
+
+ Use toIPv4Address() instead.
+*/
+
+/*!
+ \fn bool QHostAddress::isIp4Addr() const
+
+ Use protocol() instead.
+*/
+
+/*!
+ \fn bool QHostAddress::isIPv4Address() const
+
+ Use protocol() instead.
+*/
+
+/*!
+ \fn bool QHostAddress::isIPv6Address() const
+
+ Use protocol() instead.
+*/
+
+/*!
+ \since 4.5
+
+ Returns true if this IP is in the subnet described by the network
+ prefix \a subnet and netmask \a netmask.
+
+ An IP is considered to belong to a subnet if it is contained
+ between the lowest and the highest address in that subnet. In the
+ case of IP version 4, the lowest address is the network address,
+ while the highest address is the broadcast address.
+
+ The \a subnet argument does not have to be the actual network
+ address (the lowest address in the subnet). It can be any valid IP
+ belonging to that subnet. In particular, if it is equal to the IP
+ address held by this object, this function will always return true
+ (provided the netmask is a valid value).
+
+ \sa parseSubnet()
+*/
+bool QHostAddress::isInSubnet(const QHostAddress &subnet, int netmask) const
+{
+ QT_ENSURE_PARSED(this);
+ if (subnet.protocol() != d->protocol || netmask < 0)
+ return false;
+
+ union {
+ quint32 ip;
+ quint8 data[4];
+ } ip4, net4;
+ const quint8 *ip;
+ const quint8 *net;
+ if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ if (netmask > 32)
+ netmask = 32;
+ ip4.ip = qToBigEndian(d->a);
+ net4.ip = qToBigEndian(subnet.d->a);
+ ip = ip4.data;
+ net = net4.data;
+ } else if (d->protocol == QAbstractSocket::IPv6Protocol) {
+ if (netmask > 128)
+ netmask = 128;
+ ip = d->a6.c;
+ net = subnet.d->a6.c;
+ } else {
+ return false;
+ }
+
+ if (netmask >= 8 && memcmp(ip, net, netmask / 8) != 0)
+ return false;
+ if ((netmask & 7) == 0)
+ return true;
+
+ // compare the last octet now
+ quint8 bytemask = 256 - (1 << (8 - (netmask & 7)));
+ quint8 ipbyte = ip[netmask / 8];
+ quint8 netbyte = net[netmask / 8];
+ return (ipbyte & bytemask) == (netbyte & bytemask);
+}
+
+/*!
+ \since 4.5
+ \overload
+
+ Returns true if this IP is in the subnet described by \a
+ subnet. The QHostAddress member of \a subnet contains the network
+ prefix and the int (second) member contains the netmask (prefix
+ length).
+*/
+bool QHostAddress::isInSubnet(const QPair<QHostAddress, int> &subnet) const
+{
+ return isInSubnet(subnet.first, subnet.second);
+}
+
+
+/*!
+ \since 4.5
+
+ Parses the IP and subnet information contained in \a subnet and
+ returns the network prefix for that network and its prefix length.
+
+ The IP address and the netmask must be separated by a slash
+ (/).
+
+ This function supports arguments in the form:
+ \list
+ \o 123.123.123.123/n where n is any value between 0 and 32
+ \o 123.123.123.123/255.255.255.255
+ \o <ipv6-address>/n where n is any value between 0 and 128
+ \endlist
+
+ For IP version 4, this function accepts as well missing trailing
+ components (i.e., less than 4 octets, like "192.168.1"), followed
+ or not by a dot. If the netmask is also missing in that case, it
+ is set to the number of octets actually passed (in the example
+ above, it would be 24, for 3 octets).
+
+ \sa isInSubnet()
+*/
+QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
+{
+ // We support subnets in the form:
+ // ddd.ddd.ddd.ddd/nn
+ // ddd.ddd.ddd/nn
+ // ddd.ddd/nn
+ // ddd/nn
+ // ddd.ddd.ddd.
+ // ddd.ddd.ddd
+ // ddd.ddd.
+ // ddd.ddd
+ // ddd.
+ // ddd
+ // <ipv6-address>/nn
+ //
+ // where nn can be an IPv4-style netmask for the IPv4 forms
+
+ const QPair<QHostAddress, int> invalid = qMakePair(QHostAddress(), -1);
+ if (subnet.isEmpty())
+ return invalid;
+
+ int slash = subnet.indexOf(QLatin1Char('/'));
+ QString netStr = subnet;
+ if (slash != -1)
+ netStr.truncate(slash);
+
+ int netmask = -1;
+ bool isIpv6 = netStr.contains(QLatin1Char(':'));
+
+ if (slash != -1) {
+ // is the netmask given in IP-form or in bit-count form?
+ if (!isIpv6 && subnet.indexOf(QLatin1Char('.'), slash + 1) != -1) {
+ // IP-style, convert it to bit-count form
+ QNetmaskAddress parser;
+ if (!parser.setAddress(subnet.mid(slash + 1)))
+ return invalid;
+ netmask = parser.prefixLength();
+ } else {
+ bool ok;
+ netmask = subnet.mid(slash + 1).toUInt(&ok);
+ if (!ok)
+ return invalid; // failed to parse the subnet
+ }
+ }
+
+ if (isIpv6) {
+ // looks like it's an IPv6 address
+ if (netmask > 128)
+ return invalid; // invalid netmask
+ if (netmask < 0)
+ netmask = 128;
+
+ QHostAddress net;
+ if (!net.setAddress(netStr))
+ return invalid; // failed to parse the IP
+
+ clearBits(net.d->a6.c, netmask, 128);
+ return qMakePair(net, netmask);
+ }
+
+ if (netmask > 32)
+ return invalid; // invalid netmask
+
+ // parse the address manually
+ QStringList parts = netStr.split(QLatin1Char('.'));
+ if (parts.isEmpty() || parts.count() > 4)
+ return invalid; // invalid IPv4 address
+
+ if (parts.last().isEmpty())
+ parts.removeLast();
+
+ quint32 addr = 0;
+ for (int i = 0; i < parts.count(); ++i) {
+ bool ok;
+ uint byteValue = parts.at(i).toUInt(&ok);
+ if (!ok || byteValue > 255)
+ return invalid; // invalid IPv4 address
+
+ addr <<= 8;
+ addr += byteValue;
+ }
+ addr <<= 8 * (4 - parts.count());
+ if (netmask == -1) {
+ netmask = 8 * parts.count();
+ } else if (netmask == 0) {
+ // special case here
+ // x86's instructions "shr" and "shl" do not operate when
+ // their argument is 32, so the code below doesn't work as expected
+ addr = 0;
+ } else if (netmask != 32) {
+ // clear remaining bits
+ quint32 mask = quint32(0xffffffff) >> (32 - netmask) << (32 - netmask);
+ addr &= mask;
+ }
+
+ return qMakePair(QHostAddress(addr), netmask);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QHostAddress &address)
+{
+ d.maybeSpace() << "QHostAddress(" << address.toString() << ')';
+ return d.space();
+}
+#endif
+
+uint qHash(const QHostAddress &key)
+{
+ return qHash(key.toString());
+}
+
+#ifndef QT_NO_DATASTREAM
+
+/*! \relates QHostAddress
+
+ Writes host address \a address to the stream \a out and returns a reference
+ to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator<<(QDataStream &out, const QHostAddress &address)
+{
+ qint8 prot;
+ prot = qint8(address.protocol());
+ out << prot;
+ switch (address.protocol()) {
+ case QAbstractSocket::UnknownNetworkLayerProtocol:
+ break;
+ case QAbstractSocket::IPv4Protocol:
+ out << address.toIPv4Address();
+ break;
+ case QAbstractSocket::IPv6Protocol:
+ {
+ Q_IPV6ADDR ipv6 = address.toIPv6Address();
+ for (int i = 0; i < 16; ++i)
+ out << ipv6[i];
+ out << address.scopeId();
+ }
+ break;
+ }
+ return out;
+}
+
+/*! \relates QHostAddress
+
+ Reads a host address into \a address from the stream \a in and returns a
+ reference to the stream.
+
+ \sa {Serializing Qt Data Types}
+*/
+QDataStream &operator>>(QDataStream &in, QHostAddress &address)
+{
+ qint8 prot;
+ in >> prot;
+ switch (QAbstractSocket::NetworkLayerProtocol(prot)) {
+ case QAbstractSocket::UnknownNetworkLayerProtocol:
+ address.clear();
+ break;
+ case QAbstractSocket::IPv4Protocol:
+ {
+ quint32 ipv4;
+ in >> ipv4;
+ address.setAddress(ipv4);
+ }
+ break;
+ case QAbstractSocket::IPv6Protocol:
+ {
+ Q_IPV6ADDR ipv6;
+ for (int i = 0; i < 16; ++i)
+ in >> ipv6[i];
+ address.setAddress(ipv6);
+
+ QString scope;
+ in >> scope;
+ address.setScopeId(scope);
+ }
+ break;
+ default:
+ address.clear();
+ in.setStatus(QDataStream::ReadCorruptData);
+ }
+ return in;
+}
+
+#endif //QT_NO_DATASTREAM
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
new file mode 100644
index 0000000000..e626e9fefc
--- /dev/null
+++ b/src/network/kernel/qhostaddress.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHOSTADDRESS_H
+#define QHOSTADDRESS_H
+
+#include <QtCore/qpair.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qabstractsocket.h>
+
+struct sockaddr;
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QHostAddressPrivate;
+
+class Q_NETWORK_EXPORT QIPv6Address
+{
+public:
+ inline quint8 &operator [](int index) { return c[index]; }
+ inline quint8 operator [](int index) const { return c[index]; }
+ quint8 c[16];
+};
+
+typedef QIPv6Address Q_IPV6ADDR;
+
+class Q_NETWORK_EXPORT QHostAddress
+{
+public:
+ enum SpecialAddress {
+ Null,
+ Broadcast,
+ LocalHost,
+ LocalHostIPv6,
+ Any,
+ AnyIPv6
+ };
+
+ QHostAddress();
+ explicit QHostAddress(quint32 ip4Addr);
+ explicit QHostAddress(quint8 *ip6Addr);
+ explicit QHostAddress(const Q_IPV6ADDR &ip6Addr);
+ explicit QHostAddress(const sockaddr *sockaddr);
+ explicit QHostAddress(const QString &address);
+ QHostAddress(const QHostAddress &copy);
+ QHostAddress(SpecialAddress address);
+ ~QHostAddress();
+
+ QHostAddress &operator=(const QHostAddress &other);
+ QHostAddress &operator=(const QString &address);
+
+ void setAddress(quint32 ip4Addr);
+ void setAddress(quint8 *ip6Addr);
+ void setAddress(const Q_IPV6ADDR &ip6Addr);
+ void setAddress(const sockaddr *sockaddr);
+ bool setAddress(const QString &address);
+
+ QAbstractSocket::NetworkLayerProtocol protocol() const;
+ quint32 toIPv4Address() const;
+ Q_IPV6ADDR toIPv6Address() const;
+
+ QString toString() const;
+
+ QString scopeId() const;
+ void setScopeId(const QString &id);
+
+ bool operator ==(const QHostAddress &address) const;
+ bool operator ==(SpecialAddress address) const;
+ inline bool operator !=(const QHostAddress &address) const
+ { return !operator==(address); }
+ inline bool operator !=(SpecialAddress address) const
+ { return !operator==(address); }
+ bool isNull() const;
+ void clear();
+
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT quint32 ip4Addr() const { return toIPv4Address(); }
+ inline QT3_SUPPORT bool isIPv4Address() const { return protocol() == QAbstractSocket::IPv4Protocol
+ || protocol() == QAbstractSocket::UnknownNetworkLayerProtocol; }
+ inline QT3_SUPPORT bool isIp4Addr() const { return protocol() == QAbstractSocket::IPv4Protocol
+ || protocol() == QAbstractSocket::UnknownNetworkLayerProtocol; }
+ inline QT3_SUPPORT bool isIPv6Address() const { return protocol() == QAbstractSocket::IPv6Protocol; }
+#endif
+
+ bool isInSubnet(const QHostAddress &subnet, int netmask) const;
+ bool isInSubnet(const QPair<QHostAddress, int> &subnet) const;
+
+ static QPair<QHostAddress, int> parseSubnet(const QString &subnet);
+
+protected:
+ QScopedPointer<QHostAddressPrivate> d;
+};
+
+inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2)
+{ return address2 == address1; }
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QHostAddress &);
+#endif
+
+
+Q_NETWORK_EXPORT uint qHash(const QHostAddress &key);
+
+#ifndef QT_NO_DATASTREAM
+Q_NETWORK_EXPORT QDataStream &operator<<(QDataStream &, const QHostAddress &);
+Q_NETWORK_EXPORT QDataStream &operator>>(QDataStream &, QHostAddress &);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+#endif // QHOSTADDRESS_H
diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h
new file mode 100644
index 0000000000..a23b84ec69
--- /dev/null
+++ b/src/network/kernel/qhostaddress_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHOSTADDRESSPRIVATE_H
+#define QHOSTADDRESSPRIVATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the QHostAddress and QNetworkInterface classes. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+#include "qhostaddress.h"
+#include "qabstractsocket.h"
+
+class QNetmaskAddress: public QHostAddress
+{
+ int length;
+public:
+ QNetmaskAddress() : QHostAddress(), length(-1) { }
+
+ bool setAddress(const QString &address);
+ bool setAddress(const QHostAddress &address);
+
+ int prefixLength() const;
+ void setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int len);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
new file mode 100644
index 0000000000..a16d4ca451
--- /dev/null
+++ b/src/network/kernel/qhostinfo.cpp
@@ -0,0 +1,808 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhostinfo.h"
+#include "qhostinfo_p.h"
+
+#include "QtCore/qscopedpointer.h"
+#include <qabstracteventdispatcher.h>
+#include <qcoreapplication.h>
+#include <qmetaobject.h>
+#include <qstringlist.h>
+#include <qthread.h>
+#include <qurl.h>
+#include <private/qnetworksession_p.h>
+
+#ifdef Q_OS_UNIX
+# include <unistd.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define QHOSTINFO_DEBUG
+
+#ifndef Q_OS_SYMBIAN
+Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
+#else
+Q_GLOBAL_STATIC(QSymbianHostInfoLookupManager, theHostInfoLookupManager)
+#endif
+
+/*!
+ \class QHostInfo
+ \brief The QHostInfo class provides static functions for host name lookups.
+
+ \reentrant
+ \inmodule QtNetwork
+ \ingroup network
+
+ QHostInfo uses the lookup mechanisms provided by the operating
+ system to find the IP address(es) associated with a host name,
+ or the host name associated with an IP address.
+ The class provides two static convenience functions: one that
+ works asynchronously and emits a signal once the host is found,
+ and one that blocks and returns a QHostInfo object.
+
+ To look up a host's IP addresses asynchronously, call lookupHost(),
+ which takes the host name or IP address, a receiver object, and a slot
+ signature as arguments and returns an ID. You can abort the
+ lookup by calling abortHostLookup() with the lookup ID.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 0
+
+
+ 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.
+
+ If the lookup failed, error() returns the type of error that
+ occurred. errorString() gives a human-readable description of the
+ lookup error.
+
+ If you want a blocking lookup, use the QHostInfo::fromName() function:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 1
+
+ QHostInfo supports Internationalized Domain Names (IDNs) through the
+ IDNA and Punycode standards.
+
+ To retrieve the name of the local host, use the static
+ QHostInfo::localHostName() function.
+
+ \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup
+ instead of one dedicated DNS thread. This improves performance,
+ but also changes the order of signal emissions when using lookupHost()
+ compared to previous versions of Qt.
+ \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache
+ for performance improvements.
+
+ \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492}
+*/
+
+static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+/*!
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the slot or signal \a member in \a receiver is called with
+ a QHostInfo argument. The QHostInfo object can then be inspected
+ to get the results of the lookup.
+
+ The lookup is performed by a single function call, for example:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 2
+
+ The implementation of the slot prints basic information about the
+ addresses returned by the lookup, or reports an error if it failed:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 3
+
+ If you pass a literal IP address to \a name instead of a host name,
+ QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
+ perform a \e reverse lookup). On success, the resulting QHostInfo will
+ contain both the resolved domain name and IP addresses for the host
+ name. Example:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
+
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+int QHostInfo::lookupHost(const QString &name, QObject *receiver,
+ const char *member)
+{
+#if defined QHOSTINFO_DEBUG
+ 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;
+ }
+
+ qRegisterMetaType<QHostInfo>("QHostInfo");
+
+ int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
+
+ if (name.isEmpty()) {
+ QHostInfo hostInfo(id);
+ hostInfo.setError(QHostInfo::HostNotFound);
+ hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
+ QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
+ QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
+ receiver, member, Qt::QueuedConnection);
+ result.data()->emitResultsReady(hostInfo);
+ return id;
+ }
+
+#ifndef Q_OS_SYMBIAN
+ QHostInfoLookupManager *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
+ 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;
+}
+
+/*!
+ Aborts the host lookup with the ID \a id, as returned by lookupHost().
+
+ \sa lookupHost(), lookupId()
+*/
+void QHostInfo::abortHostLookup(int id)
+{
+ theHostInfoLookupManager()->abortLookup(id);
+}
+
+/*!
+ Looks up the IP address(es) for the given host \a name. The
+ function blocks during the lookup which means that execution of
+ the program is suspended until the results of the lookup are
+ ready. Returns the result of the lookup in a QHostInfo object.
+
+ If you pass a literal IP address to \a name instead of a host name,
+ QHostInfo will search for the domain name for the IP (i.e., QHostInfo will
+ perform a \e reverse lookup). On success, the returned QHostInfo will
+ contain both the resolved domain name and IP addresses for the host name.
+
+ \sa lookupHost()
+*/
+QHostInfo QHostInfo::fromName(const QString &name)
+{
+#if defined QHOSTINFO_DEBUG
+ qDebug("QHostInfo::fromName(\"%s\")",name.toLatin1().constData());
+#endif
+
+ QHostInfo hostInfo = QHostInfoAgent::fromName(name);
+ 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> networkSession)
+{
+ return QHostInfoAgent::fromName(hostName);
+}
+#endif
+
+
+/*!
+ \enum QHostInfo::HostInfoError
+
+ This enum describes the various errors that can occur when trying
+ to resolve a host name.
+
+ \value NoError The lookup was successful.
+ \value HostNotFound No IP addresses were found for the host.
+ \value UnknownError An unknown error occurred.
+
+ \sa error(), setError()
+*/
+
+/*!
+ Constructs an empty host info object with lookup ID \a id.
+
+ \sa lookupId()
+*/
+QHostInfo::QHostInfo(int id)
+ : d(new QHostInfoPrivate)
+{
+ d->lookupId = id;
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QHostInfo::QHostInfo(const QHostInfo &other)
+ : d(new QHostInfoPrivate(*other.d.data()))
+{
+}
+
+/*!
+ Assigns the data of the \a other object to this host info object,
+ and returns a reference to it.
+*/
+QHostInfo &QHostInfo::operator=(const QHostInfo &other)
+{
+ *d.data() = *other.d.data();
+ return *this;
+}
+
+/*!
+ Destroys the host info object.
+*/
+QHostInfo::~QHostInfo()
+{
+}
+
+/*!
+ Returns the list of IP addresses associated with hostName(). This
+ list may be empty.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 5
+
+ \sa hostName(), error()
+*/
+QList<QHostAddress> QHostInfo::addresses() const
+{
+ return d->addrs;
+}
+
+/*!
+ Sets the list of addresses in this QHostInfo to \a addresses.
+
+ \sa addresses()
+*/
+void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
+{
+ d->addrs = addresses;
+}
+
+/*!
+ Returns the name of the host whose IP addresses were looked up.
+
+ \sa localHostName()
+*/
+QString QHostInfo::hostName() const
+{
+ return d->hostName;
+}
+
+/*!
+ Sets the host name of this QHostInfo to \a hostName.
+
+ \sa hostName()
+*/
+void QHostInfo::setHostName(const QString &hostName)
+{
+ d->hostName = hostName;
+}
+
+/*!
+ Returns the type of error that occurred if the host name lookup
+ failed; otherwise returns NoError.
+
+ \sa setError(), errorString()
+*/
+QHostInfo::HostInfoError QHostInfo::error() const
+{
+ return d->err;
+}
+
+/*!
+ Sets the error type of this QHostInfo to \a error.
+
+ \sa error(), errorString()
+*/
+void QHostInfo::setError(HostInfoError error)
+{
+ d->err = error;
+}
+
+/*!
+ Returns the ID of this lookup.
+
+ \sa setLookupId(), abortHostLookup(), hostName()
+*/
+int QHostInfo::lookupId() const
+{
+ return d->lookupId;
+}
+
+/*!
+ Sets the ID of this lookup to \a id.
+
+ \sa lookupId(), lookupHost()
+*/
+void QHostInfo::setLookupId(int id)
+{
+ d->lookupId = id;
+}
+
+/*!
+ If the lookup failed, this function returns a human readable
+ description of the error; otherwise "Unknown error" is returned.
+
+ \sa setErrorString(), error()
+*/
+QString QHostInfo::errorString() const
+{
+ return d->errorStr;
+}
+
+/*!
+ Sets the human readable description of the error that occurred to \a str
+ if the lookup failed.
+
+ \sa errorString(), setError()
+*/
+void QHostInfo::setErrorString(const QString &str)
+{
+ d->errorStr = str;
+}
+
+/*!
+ \fn QString QHostInfo::localHostName()
+
+ Returns the host name of this machine.
+
+ \sa hostName()
+*/
+
+/*!
+ \fn QString QHostInfo::localDomainName()
+
+ Returns the DNS domain of this machine.
+
+ Note: DNS domains are not related to domain names found in
+ Windows networks.
+
+ \sa hostName()
+*/
+
+#ifndef Q_OS_SYMBIAN
+QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
+{
+ setAutoDelete(true);
+}
+
+// the QHostInfoLookupManager will at some point call this via a QThreadPool
+void QHostInfoRunnable::run()
+{
+ QHostInfoLookupManager *manager = theHostInfoLookupManager();
+ // check aborted
+ if (manager->wasAborted(id)) {
+ manager->lookupFinished(this);
+ return;
+ }
+
+ QHostInfo hostInfo;
+
+ // QHostInfo::lookupHost already checks the cache. However we need to check
+ // it here too because it might have been cache saved by another QHostInfoRunnable
+ // in the meanwhile while this QHostInfoRunnable was scheduled but not running
+ if (manager->cache.isEnabled()) {
+ // check the cache first
+ bool valid = false;
+ hostInfo = manager->cache.get(toBeLookedUp, &valid);
+ if (!valid) {
+ // not in cache, we need to do the lookup and store the result in the cache
+ hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
+ manager->cache.put(toBeLookedUp, hostInfo);
+ }
+ } else {
+ // cache is not enabled, just do the lookup and continue
+ hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
+ }
+
+ // check aborted again
+ if (manager->wasAborted(id)) {
+ manager->lookupFinished(this);
+ return;
+ }
+
+ // signal emission
+ hostInfo.setLookupId(id);
+ resultEmitter.emitResultsReady(hostInfo);
+
+ // now also iterate through the postponed ones
+ {
+ QMutexLocker locker(&manager->mutex);
+ QMutableListIterator<QHostInfoRunnable*> iterator(manager->postponedLookups);
+ while (iterator.hasNext()) {
+ QHostInfoRunnable* postponed = iterator.next();
+ if (toBeLookedUp == postponed->toBeLookedUp) {
+ // we can now emit
+ iterator.remove();
+ hostInfo.setLookupId(postponed->id);
+ postponed->resultEmitter.emitResultsReady(hostInfo);
+ }
+ }
+ }
+
+ manager->lookupFinished(this);
+
+ // thread goes back to QThreadPool
+}
+
+QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
+{
+ moveToThread(QCoreApplicationPrivate::mainThread());
+ connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
+ threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
+}
+
+QHostInfoLookupManager::~QHostInfoLookupManager()
+{
+ wasDeleted = true;
+
+ // don't qDeleteAll currentLookups, the QThreadPool has ownership
+ clear();
+}
+
+void QHostInfoLookupManager::clear()
+{
+ {
+ QMutexLocker locker(&mutex);
+ qDeleteAll(postponedLookups);
+ qDeleteAll(scheduledLookups);
+ qDeleteAll(finishedLookups);
+ postponedLookups.clear();
+ scheduledLookups.clear();
+ finishedLookups.clear();
+ }
+
+ threadPool.waitForDone();
+ cache.clear();
+}
+
+void QHostInfoLookupManager::work()
+{
+ if (wasDeleted)
+ return;
+
+ // goals of this function:
+ // - launch new lookups via the thread pool
+ // - make sure only one lookup per host/IP is in progress
+
+ QMutexLocker locker(&mutex);
+
+ if (!finishedLookups.isEmpty()) {
+ // remove ID from aborted if it is in there
+ for (int i = 0; i < finishedLookups.length(); i++) {
+ abortedLookups.removeAll(finishedLookups.at(i)->id);
+ }
+
+ finishedLookups.clear();
+ }
+
+ if (!postponedLookups.isEmpty()) {
+ // try to start the postponed ones
+
+ QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
+ while (iterator.hasNext()) {
+ QHostInfoRunnable* postponed = iterator.next();
+
+ // check if none of the postponed hostnames is currently running
+ bool alreadyRunning = false;
+ for (int i = 0; i < currentLookups.length(); i++) {
+ if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
+ alreadyRunning = true;
+ break;
+ }
+ }
+ if (!alreadyRunning) {
+ iterator.remove();
+ scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
+ }
+ }
+ }
+
+ if (!scheduledLookups.isEmpty()) {
+ // try to start the new ones
+ QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
+ while (iterator.hasNext()) {
+ QHostInfoRunnable *scheduled = iterator.next();
+
+ // check if a lookup for this host is already running, then postpone
+ for (int i = 0; i < currentLookups.size(); i++) {
+ if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
+ iterator.remove();
+ postponedLookups.append(scheduled);
+ scheduled = 0;
+ break;
+ }
+ }
+
+ if (scheduled && currentLookups.size() < threadPool.maxThreadCount()) {
+ // runnable now running in new thread, track this in currentLookups
+ threadPool.start(scheduled);
+ iterator.remove();
+ currentLookups.append(scheduled);
+ } else {
+ // was postponed, continue iterating
+ continue;
+ }
+ };
+ }
+}
+
+// called by QHostInfo
+void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
+{
+ if (wasDeleted)
+ return;
+
+ QMutexLocker locker(&this->mutex);
+ scheduledLookups.enqueue(r);
+ work();
+}
+
+// called by QHostInfo
+void QHostInfoLookupManager::abortLookup(int id)
+{
+ if (wasDeleted)
+ return;
+
+ QMutexLocker locker(&this->mutex);
+
+ // is postponed? delete and return
+ for (int i = 0; i < postponedLookups.length(); i++) {
+ if (postponedLookups.at(i)->id == id) {
+ delete postponedLookups.takeAt(i);
+ return;
+ }
+ }
+
+ // is scheduled? delete and return
+ for (int i = 0; i < scheduledLookups.length(); i++) {
+ if (scheduledLookups.at(i)->id == id) {
+ delete scheduledLookups.takeAt(i);
+ return;
+ }
+ }
+
+ if (!abortedLookups.contains(id))
+ abortedLookups.append(id);
+}
+
+// called from QHostInfoRunnable
+bool QHostInfoLookupManager::wasAborted(int id)
+{
+ if (wasDeleted)
+ return true;
+
+ QMutexLocker locker(&this->mutex);
+ return abortedLookups.contains(id);
+}
+
+// called from QHostInfoRunnable
+void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
+{
+ if (wasDeleted)
+ return;
+
+ QMutexLocker locker(&this->mutex);
+ currentLookups.removeOne(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)
+{
+ *valid = false;
+ *id = -1;
+
+ // check cache
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (manager && manager->cache.isEnabled()) {
+ QHostInfo info = manager->cache.get(name, valid);
+ if (*valid) {
+ return info;
+ }
+ }
+
+ // was not in cache, trigger lookup
+ *id = QHostInfo::lookupHost(name, receiver, member);
+
+ // return empty response, valid==false
+ return QHostInfo();
+}
+
+void qt_qhostinfo_clear_cache()
+{
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (manager) {
+ manager->clear();
+ }
+}
+
+void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
+{
+ QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ if (manager) {
+ manager->cache.setEnabled(e);
+ }
+}
+
+// cache for 60 seconds
+// cache 64 items
+QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(64)
+{
+#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
+ enabled = false;
+#endif
+}
+
+bool QHostInfoCache::isEnabled()
+{
+ return enabled;
+}
+
+// this function is currently only used for the auto tests
+// and not usable by public API
+void QHostInfoCache::setEnabled(bool e)
+{
+ enabled = e;
+}
+
+
+QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
+{
+ QMutexLocker locker(&this->mutex);
+
+ *valid = false;
+ if (cache.contains(name)) {
+ QHostInfoCacheElement *element = cache.object(name);
+ if (element->age.elapsed() < max_age*1000)
+ *valid = true;
+ return element->info;
+
+ // FIXME idea:
+ // if too old but not expired, trigger a new lookup
+ // to freshen our cache
+ }
+
+ return QHostInfo();
+}
+
+void QHostInfoCache::put(const QString &name, const QHostInfo &info)
+{
+ // if the lookup failed, don't cache
+ if (info.error() != QHostInfo::NoError)
+ return;
+
+ QHostInfoCacheElement* element = new QHostInfoCacheElement();
+ element->info = info;
+ element->age = QElapsedTimer();
+ element->age.start();
+
+ QMutexLocker locker(&this->mutex);
+ cache.insert(name, element); // cache will take ownership
+}
+
+void QHostInfoCache::clear()
+{
+ QMutexLocker locker(&this->mutex);
+ cache.clear();
+}
+
+QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
+{
+ return theHostInfoLookupManager();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
new file mode 100644
index 0000000000..33453e76ce
--- /dev/null
+++ b/src/network/kernel/qhostinfo.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHOSTINFO_H
+#define QHOSTINFO_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qhostaddress.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QObject;
+class QHostInfoPrivate;
+
+class Q_NETWORK_EXPORT QHostInfo
+{
+public:
+ enum HostInfoError {
+ NoError,
+ HostNotFound,
+ UnknownError
+ };
+
+ QHostInfo(int lookupId = -1);
+ QHostInfo(const QHostInfo &d);
+ QHostInfo &operator=(const QHostInfo &d);
+ ~QHostInfo();
+
+ QString hostName() const;
+ void setHostName(const QString &name);
+
+ QList<QHostAddress> addresses() const;
+ void setAddresses(const QList<QHostAddress> &addresses);
+
+ HostInfoError error() const;
+ void setError(HostInfoError error);
+
+ QString errorString() const;
+ void setErrorString(const QString &errorString);
+
+ void setLookupId(int id);
+ int lookupId() const;
+
+ static int lookupHost(const QString &name, QObject *receiver, const char *member);
+ static void abortHostLookup(int lookupId);
+
+ static QHostInfo fromName(const QString &name);
+ static QString localHostName();
+ static QString localDomainName();
+
+private:
+ QScopedPointer<QHostInfoPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QHOSTINFO_H
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
new file mode 100644
index 0000000000..8da069262a
--- /dev/null
+++ b/src/network/kernel/qhostinfo_p.h
@@ -0,0 +1,319 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHOSTINFO_P_H
+#define QHOSTINFO_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 QHostInfo class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qcoreapplication.h"
+#include "private/qcoreapplication_p.h"
+#include "QtNetwork/qhostinfo.h"
+#include "QtCore/qmutex.h"
+#include "QtCore/qwaitcondition.h"
+#include "QtCore/qobject.h"
+#include "QtCore/qpointer.h"
+#include "QtCore/qthread.h"
+#include "QtCore/qthreadpool.h"
+#include "QtCore/qmutex.h"
+#include "QtCore/qrunnable.h"
+#include "QtCore/qlist.h"
+#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
+public Q_SLOTS:
+ inline void emitResultsReady(const QHostInfo &info)
+ {
+ emit resultsReady(info);
+ }
+
+Q_SIGNALS:
+ void resultsReady(const QHostInfo &info);
+};
+
+// needs to be QObject because fromName calls tr()
+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
+{
+public:
+ inline QHostInfoPrivate()
+ : err(QHostInfo::NoError),
+ errorStr(QLatin1String(QT_TRANSLATE_NOOP("QHostInfo", "Unknown error"))),
+ 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;
+ QList<QHostAddress> addrs;
+ QString hostName;
+ int lookupId;
+};
+
+// 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);
+void Q_AUTOTEST_EXPORT qt_qhostinfo_clear_cache();
+void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e);
+
+class QHostInfoCache
+{
+public:
+ QHostInfoCache();
+ const int max_age; // seconds
+
+ QHostInfo get(const QString &name, bool *valid);
+ void put(const QString &name, const QHostInfo &info);
+ void clear();
+
+ bool isEnabled();
+ void setEnabled(bool e);
+private:
+ bool enabled;
+ struct QHostInfoCacheElement {
+ QHostInfo info;
+ QElapsedTimer age;
+ };
+ QCache<QString,QHostInfoCacheElement> cache;
+ QMutex mutex;
+};
+
+// the following classes are used for the (normal) case: We use multiple threads to lookup DNS
+
+class QHostInfoRunnable : public QRunnable
+{
+public:
+ QHostInfoRunnable (QString hn, int i);
+ void run();
+
+ QString toBeLookedUp;
+ int id;
+ QHostInfoResult resultEmitter;
+};
+
+
+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:
+ QHostInfoLookupManager();
+ ~QHostInfoLookupManager();
+
+ void clear();
+ void work();
+
+ // called from QHostInfo
+ void scheduleLookup(QHostInfoRunnable *r);
+ void abortLookup(int id);
+
+ // called from QHostInfoRunnable
+ void lookupFinished(QHostInfoRunnable *r);
+ bool wasAborted(int id);
+
+ friend class QHostInfoRunnable;
+protected:
+ QList<QHostInfoRunnable*> currentLookups; // in progress
+ QList<QHostInfoRunnable*> postponedLookups; // postponed because in progress for same host
+ QQueue<QHostInfoRunnable*> scheduledLookups; // not yet started
+ QList<QHostInfoRunnable*> finishedLookups; // recently finished
+ QList<int> abortedLookups; // ids of aborted lookups
+
+ QThreadPool threadPool;
+
+ QMutex mutex;
+
+ bool wasDeleted;
+
+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 0000000000..2a8de1d2de
--- /dev/null
+++ b/src/network/kernel/qhostinfo_symbian.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#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>
+
+// 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;
+ // 39 is the maximum length of an IPv6 address.
+ TBuf<39> ipAddr;
+
+ // Fill ipAddr with the IP address from hostAdd
+ hostAdd.Output(ipAddr);
+ if (ipAddr.Length() > 0)
+ hostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr)));
+
+ // Check if there's more than one IP address linkd to this name
+ while (hostResolver.Next(nameResult) == KErrNone) {
+ hostAdd = nameResult().iAddr;
+ hostAdd.Output(ipAddr);
+
+ // Ensure that record is valid (not an alias and with length greater than 0)
+ if (!(nameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified())) {
+ hostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr)));
+ }
+ }
+
+ 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;
+ // 39 is the maximum length of an IPv6 address.
+ TBuf<39> ipAddr;
+
+ hostAdd.Output(ipAddr);
+
+ // Ensure that record is valid (not an alias and with length greater than 0)
+ if (!(iNameResult().iFlags & TNameRecord::EAlias) && !(hostAdd.IsUnspecified())) {
+ iHostAddresses.append(QHostAddress(qt_TDesC2QString(ipAddr)));
+ }
+
+ 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
new file mode 100644
index 0000000000..8fc6bf6504
--- /dev/null
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -0,0 +1,396 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHOSTINFO_DEBUG
+
+#include "qplatformdefs.h"
+
+#include "qhostinfo_p.h"
+#include "private/qnativesocketengine_p.h"
+#include "qiodevice.h"
+#include <qbytearray.h>
+#include <qlibrary.h>
+#include <qurl.h>
+#include <qfile.h>
+#include <private/qmutexpool_p.h>
+#include <private/qnet_unix_p.h>
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#if defined(Q_OS_VXWORKS)
+# include <hostLib.h>
+#else
+# include <resolv.h>
+#endif
+
+#if defined (QT_NO_GETADDRINFO)
+#include <qmutex.h>
+QT_BEGIN_NAMESPACE
+Q_GLOBAL_STATIC(QMutex, getHostByNameMutex)
+QT_END_NAMESPACE
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Almost always the same. If not, specify in qplatformdefs.h.
+#if !defined(QT_SOCKOPTLEN_T)
+# define QT_SOCKOPTLEN_T QT_SOCKLEN_T
+#endif
+
+// HP-UXi has a bug in getaddrinfo(3) that makes it thread-unsafe
+// with this flag. So disable it in that platform.
+#if defined(AI_ADDRCONFIG) && !defined(Q_OS_HPUX)
+# define Q_ADDRCONFIG AI_ADDRCONFIG
+#endif
+
+typedef struct __res_state *res_state_ptr;
+
+typedef int (*res_init_proto)(void);
+static res_init_proto local_res_init = 0;
+typedef int (*res_ninit_proto)(res_state_ptr);
+static res_ninit_proto local_res_ninit = 0;
+typedef void (*res_nclose_proto)(res_state_ptr);
+static res_nclose_proto local_res_nclose = 0;
+static res_state_ptr local_res = 0;
+
+static void resolveLibrary()
+{
+#ifndef QT_NO_LIBRARY
+ QLibrary lib(QLatin1String("resolv"));
+ if (!lib.load())
+ return;
+
+ local_res_init = res_init_proto(lib.resolve("__res_init"));
+ if (!local_res_init)
+ local_res_init = res_init_proto(lib.resolve("res_init"));
+
+ local_res_ninit = res_ninit_proto(lib.resolve("__res_ninit"));
+ if (!local_res_ninit)
+ local_res_ninit = res_ninit_proto(lib.resolve("res_ninit"));
+
+ if (!local_res_ninit) {
+ // if we can't get a thread-safe context, we have to use the global _res state
+ local_res = res_state_ptr(lib.resolve("_res"));
+ } else {
+ local_res_nclose = res_nclose_proto(lib.resolve("res_nclose"));
+ if (!local_res_nclose)
+ local_res_nclose = res_nclose_proto(lib.resolve("__res_nclose"));
+ if (!local_res_nclose)
+ local_res_ninit = 0;
+ }
+#endif
+}
+
+QHostInfo QHostInfoAgent::fromName(const QString &hostName)
+{
+ QHostInfo results;
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("QHostInfoAgent::fromName(%s) looking up...",
+ hostName.toLatin1().constData());
+#endif
+
+ // Load res_init on demand.
+ static volatile bool triedResolve = false;
+ if (!triedResolve) {
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_init));
+ if (!triedResolve) {
+ resolveLibrary();
+ triedResolve = true;
+ }
+ }
+
+ // If res_init is available, poll it.
+ if (local_res_init)
+ local_res_init();
+
+ QHostAddress address;
+ 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)
+ sockaddr_in sa4;
+#ifndef QT_NO_IPV6
+ sockaddr_in6 sa6;
+#endif
+ sockaddr *sa = 0;
+ QT_SOCKLEN_T saSize = 0;
+ if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+ sa = (sockaddr *)&sa4;
+ saSize = sizeof(sa4);
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
+ }
+#ifndef QT_NO_IPV6
+ else {
+ sa = (sockaddr *)&sa6;
+ saSize = sizeof(sa6);
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ memcpy(sa6.sin6_addr.s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.s6_addr));
+ }
+#endif
+
+ char hbuf[NI_MAXHOST];
+ if (sa && getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
+ results.setHostName(QString::fromLatin1(hbuf));
+#else
+ in_addr_t inetaddr = qt_safe_inet_addr(hostName.toLatin1().constData());
+ struct hostent *ent = gethostbyaddr((const char *)&inetaddr, sizeof(inetaddr), AF_INET);
+ if (ent)
+ results.setHostName(QString::fromLatin1(ent->h_name));
+#endif
+
+ if (results.hostName().isEmpty())
+ results.setHostName(address.toString());
+ 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;
+ }
+
+#if !defined (QT_NO_GETADDRINFO)
+ // Call getaddrinfo, and place all IPv4 addresses at the start and
+ // the IPv6 addresses at the end of the address list in results.
+ addrinfo *res = 0;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+#ifdef Q_ADDRCONFIG
+ hints.ai_flags = Q_ADDRCONFIG;
+#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;
+ result = getaddrinfo(aceHostname.constData(), 0, &hints, &res);
+ }
+# endif
+
+ if (result == 0) {
+ addrinfo *node = res;
+ QList<QHostAddress> addresses;
+ while (node) {
+#ifdef QHOSTINFO_DEBUG
+ qDebug() << "getaddrinfo node: flags:" << node->ai_flags << "family:" << node->ai_family << "ai_socktype:" << node->ai_socktype << "ai_protocol:" << node->ai_protocol << "ai_addrlen:" << node->ai_addrlen;
+#endif
+ if (node->ai_family == AF_INET) {
+ QHostAddress addr;
+ addr.setAddress(ntohl(((sockaddr_in *) node->ai_addr)->sin_addr.s_addr));
+ if (!addresses.contains(addr))
+ addresses.append(addr);
+ }
+#ifndef QT_NO_IPV6
+ else if (node->ai_family == AF_INET6) {
+ QHostAddress addr;
+ sockaddr_in6 *sa6 = (sockaddr_in6 *) node->ai_addr;
+ addr.setAddress(sa6->sin6_addr.s6_addr);
+ if (sa6->sin6_scope_id)
+ addr.setScopeId(QString::number(sa6->sin6_scope_id));
+ if (!addresses.contains(addr))
+ addresses.append(addr);
+ }
+#endif
+ node = node->ai_next;
+ }
+ if (addresses.isEmpty() && node == 0) {
+ // Reached the end of the list, but no addresses were found; this
+ // means the list contains one or more unknown address types.
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(tr("Unknown address type"));
+ }
+
+ results.setAddresses(addresses);
+ freeaddrinfo(res);
+ } else if (result == EAI_NONAME
+ || result == EAI_FAIL
+#ifdef EAI_NODATA
+ // EAI_NODATA is deprecated in RFC 3493
+ || result == EAI_NODATA
+#endif
+ ) {
+ results.setError(QHostInfo::HostNotFound);
+ results.setErrorString(tr("Host not found"));
+ } else {
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(QString::fromLocal8Bit(gai_strerror(result)));
+ }
+
+#else
+ // Fall back to gethostbyname for platforms that don't define
+ // getaddrinfo. gethostbyname does not support IPv6, and it's not
+ // reentrant on all platforms. For now this is okay since we only
+ // use one QHostInfoAgent, but if more agents are introduced, locking
+ // must be provided.
+ QMutexLocker locker(::getHostByNameMutex());
+ hostent *result = gethostbyname(aceHostname.constData());
+ if (result) {
+ if (result->h_addrtype == AF_INET) {
+ QList<QHostAddress> addresses;
+ for (char **p = result->h_addr_list; *p != 0; p++) {
+ QHostAddress addr;
+ addr.setAddress(ntohl(*((quint32 *)*p)));
+ if (!addresses.contains(addr))
+ addresses.prepend(addr);
+ }
+ results.setAddresses(addresses);
+ } else {
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(tr("Unknown address type"));
+ }
+#if !defined(Q_OS_VXWORKS)
+ } else if (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA
+ || h_errno == NO_ADDRESS) {
+ results.setError(QHostInfo::HostNotFound);
+ results.setErrorString(tr("Host not found"));
+#endif
+ } else {
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(tr("Unknown error"));
+ }
+#endif // !defined (QT_NO_GETADDRINFO)
+
+#if defined(QHOSTINFO_DEBUG)
+ if (results.error() != QHostInfo::NoError) {
+ qDebug("QHostInfoAgent::fromName(): error #%d %s",
+ h_errno, results.errorString().toLatin1().constData());
+ } else {
+ QString tmp;
+ QList<QHostAddress> addresses = results.addresses();
+ for (int i = 0; i < addresses.count(); ++i) {
+ if (i != 0) tmp += ", ";
+ tmp += addresses.at(i).toString();
+ }
+ qDebug("QHostInfoAgent::fromName(): found %i entries for \"%s\": {%s}",
+ addresses.count(), hostName.toLatin1().constData(),
+ tmp.toLatin1().constData());
+ }
+#endif
+ return results;
+}
+
+QString QHostInfo::localHostName()
+{
+ char hostName[512];
+ if (gethostname(hostName, sizeof(hostName)) == -1)
+ return QString();
+ hostName[sizeof(hostName) - 1] = '\0';
+ return QString::fromLocal8Bit(hostName);
+}
+
+QString QHostInfo::localDomainName()
+{
+#if !defined(Q_OS_VXWORKS)
+ resolveLibrary();
+ if (local_res_ninit) {
+ // using thread-safe version
+ res_state_ptr state = res_state_ptr(qMalloc(sizeof(*state)));
+ Q_CHECK_PTR(state);
+ memset(state, 0, sizeof(*state));
+ local_res_ninit(state);
+ QString domainName = QUrl::fromAce(state->defdname);
+ if (domainName.isEmpty())
+ domainName = QUrl::fromAce(state->dnsrch[0]);
+ local_res_nclose(state);
+ qFree(state);
+
+ return domainName;
+ }
+
+ if (local_res_init && local_res) {
+ // using thread-unsafe version
+
+#if defined(QT_NO_GETADDRINFO)
+ // We have to call res_init to be sure that _res was initialized
+ // So, for systems without getaddrinfo (which is thread-safe), we lock the mutex too
+ QMutexLocker locker(::getHostByNameMutex());
+#endif
+ local_res_init();
+ QString domainName = QUrl::fromAce(local_res->defdname);
+ if (domainName.isEmpty())
+ domainName = QUrl::fromAce(local_res->dnsrch[0]);
+ return domainName;
+ }
+#endif
+ // nothing worked, try doing it by ourselves:
+ QFile resolvconf;
+#if defined(_PATH_RESCONF)
+ resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
+#else
+ resolvconf.setFileName(QLatin1String("/etc/resolv.conf"));
+#endif
+ if (!resolvconf.open(QIODevice::ReadOnly))
+ return QString(); // failure
+
+ QString domainName;
+ while (!resolvconf.atEnd()) {
+ QByteArray line = resolvconf.readLine().trimmed();
+ if (line.startsWith("domain "))
+ return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed());
+
+ // in case there's no "domain" line, fall back to the first "search" entry
+ if (domainName.isEmpty() && line.startsWith("search ")) {
+ QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed();
+ int pos = searchDomain.indexOf(' ');
+ if (pos != -1)
+ searchDomain.truncate(pos);
+ domainName = QUrl::fromAce(searchDomain);
+ }
+ }
+
+ // return the fallen-back-to searched domain
+ return domainName;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp
new file mode 100644
index 0000000000..58f309b80b
--- /dev/null
+++ b/src/network/kernel/qhostinfo_win.cpp
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <winsock2.h>
+
+#include "qhostinfo_p.h"
+#include "private/qnativesocketengine_p.h"
+#include <ws2tcpip.h>
+#include <private/qsystemlibrary_p.h>
+#include <qmutex.h>
+#include <qurl.h>
+#include <private/qmutexpool_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define QHOSTINFO_DEBUG
+
+// Older SDKs do not include the addrinfo struct declaration, so we
+// include a copy of it here.
+struct qt_addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ char *ai_canonname;
+ sockaddr *ai_addr;
+ qt_addrinfo *ai_next;
+};
+
+//###
+#define QT_SOCKLEN_T int
+#ifndef NI_MAXHOST // already defined to 1025 in ws2tcpip.h?
+#define NI_MAXHOST 1024
+#endif
+
+typedef int (__stdcall *getnameinfoProto)(const sockaddr *, QT_SOCKLEN_T, const char *, DWORD, const char *, DWORD, int);
+typedef int (__stdcall *getaddrinfoProto)(const char *, const char *, const qt_addrinfo *, qt_addrinfo **);
+typedef int (__stdcall *freeaddrinfoProto)(qt_addrinfo *);
+static getnameinfoProto local_getnameinfo = 0;
+static getaddrinfoProto local_getaddrinfo = 0;
+static freeaddrinfoProto local_freeaddrinfo = 0;
+
+static void resolveLibrary()
+{
+ // Attempt to resolve getaddrinfo(); without it we'll have to fall
+ // back to gethostbyname(), which has no IPv6 support.
+#if !defined(Q_OS_WINCE)
+ local_getaddrinfo = (getaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2_32"), "getaddrinfo");
+ local_freeaddrinfo = (freeaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2_32"), "freeaddrinfo");
+ local_getnameinfo = (getnameinfoProto) QSystemLibrary::resolve(QLatin1String("ws2_32"), "getnameinfo");
+#else
+ local_getaddrinfo = (getaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2"), "getaddrinfo");
+ local_freeaddrinfo = (freeaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2"), "freeaddrinfo");
+ local_getnameinfo = (getnameinfoProto) QSystemLibrary::resolve(QLatin1String("ws2"), "getnameinfo");
+#endif
+}
+
+#if defined(Q_OS_WINCE)
+#include <qmutex.h>
+QMutex qPrivCEMutex;
+#endif
+
+QHostInfo QHostInfoAgent::fromName(const QString &hostName)
+{
+#if defined(Q_OS_WINCE)
+ QMutexLocker locker(&qPrivCEMutex);
+#endif
+ QWindowsSockInit winSock;
+
+ // Load res_init on demand.
+ static volatile bool triedResolve = false;
+ if (!triedResolve) {
+ QMutexLocker locker(QMutexPool::globalInstanceGet(&local_getaddrinfo));
+ if (!triedResolve) {
+ resolveLibrary();
+ triedResolve = true;
+ }
+ }
+
+ QHostInfo results;
+
+#if defined(QHOSTINFO_DEBUG)
+ qDebug("QHostInfoAgent::fromName(%p): looking up \"%s\" (IPv6 support is %s)",
+ this, hostName.toLatin1().constData(),
+ (local_getaddrinfo && local_freeaddrinfo) ? "enabled" : "disabled");
+#endif
+
+ QHostAddress address;
+ if (address.setAddress(hostName)) {
+ // Reverse lookup
+ if (local_getnameinfo) {
+ sockaddr_in sa4;
+ qt_sockaddr_in6 sa6;
+ sockaddr *sa;
+ QT_SOCKLEN_T saSize;
+ if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+ sa = (sockaddr *)&sa4;
+ saSize = sizeof(sa4);
+ memset(&sa4, 0, sizeof(sa4));
+ sa4.sin_family = AF_INET;
+ sa4.sin_addr.s_addr = htonl(address.toIPv4Address());
+ } else {
+ sa = (sockaddr *)&sa6;
+ saSize = sizeof(sa6);
+ memset(&sa6, 0, sizeof(sa6));
+ sa6.sin6_family = AF_INET6;
+ memcpy(sa6.sin6_addr.qt_s6_addr, address.toIPv6Address().c, sizeof(sa6.sin6_addr.qt_s6_addr));
+ }
+
+ char hbuf[NI_MAXHOST];
+ if (local_getnameinfo(sa, saSize, hbuf, sizeof(hbuf), 0, 0, 0) == 0)
+ results.setHostName(QString::fromLatin1(hbuf));
+ } else {
+ unsigned long addr = inet_addr(hostName.toLatin1().constData());
+ struct hostent *ent = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
+ if (ent)
+ results.setHostName(QString::fromLatin1(ent->h_name));
+ }
+
+ if (results.hostName().isEmpty())
+ results.setHostName(address.toString());
+ 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() ? tr("No host name given") : tr("Invalid hostname"));
+ return results;
+ }
+
+ if (local_getaddrinfo && local_freeaddrinfo) {
+ // Call getaddrinfo, and place all IPv4 addresses at the start
+ // and the IPv6 addresses at the end of the address list in
+ // results.
+ qt_addrinfo *res;
+ int err = local_getaddrinfo(aceHostname.constData(), 0, 0, &res);
+ if (err == 0) {
+ QList<QHostAddress> addresses;
+ for (qt_addrinfo *p = res; p != 0; p = p->ai_next) {
+ switch (p->ai_family) {
+ case AF_INET: {
+ QHostAddress addr;
+ addr.setAddress(ntohl(((sockaddr_in *) p->ai_addr)->sin_addr.s_addr));
+ if (!addresses.contains(addr))
+ addresses.append(addr);
+ }
+ break;
+ case AF_INET6: {
+ QHostAddress addr;
+ addr.setAddress(((qt_sockaddr_in6 *) p->ai_addr)->sin6_addr.qt_s6_addr);
+ if (!addresses.contains(addr))
+ addresses.append(addr);
+ }
+ break;
+ default:
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(tr("Unknown address type"));
+ }
+ }
+ results.setAddresses(addresses);
+ local_freeaddrinfo(res);
+ } else if (WSAGetLastError() == WSAHOST_NOT_FOUND || WSAGetLastError() == WSANO_DATA) {
+ results.setError(QHostInfo::HostNotFound);
+ results.setErrorString(tr("Host not found"));
+ } else {
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(tr("Unknown error"));
+ }
+ } else {
+ // Fall back to gethostbyname, which only supports IPv4.
+ hostent *ent = gethostbyname(aceHostname.constData());
+ if (ent) {
+ char **p;
+ QList<QHostAddress> addresses;
+ switch (ent->h_addrtype) {
+ case AF_INET:
+ for (p = ent->h_addr_list; *p != 0; p++) {
+ long *ip4Addr = (long *) *p;
+ QHostAddress temp;
+ temp.setAddress(ntohl(*ip4Addr));
+ addresses << temp;
+ }
+ break;
+ default:
+ results.setError(QHostInfo::UnknownError);
+ results.setErrorString(tr("Unknown address type"));
+ break;
+ }
+ results.setAddresses(addresses);
+ } else if (WSAGetLastError() == 11001) {
+ results.setErrorString(tr("Host not found"));
+ results.setError(QHostInfo::HostNotFound);
+ } else {
+ results.setErrorString(tr("Unknown error"));
+ results.setError(QHostInfo::UnknownError);
+ }
+ }
+
+#if defined(QHOSTINFO_DEBUG)
+ if (results.error() != QHostInfo::NoError) {
+ qDebug("QHostInfoAgent::run(%p): error (%s)",
+ this, results.errorString().toLatin1().constData());
+ } else {
+ QString tmp;
+ QList<QHostAddress> addresses = results.addresses();
+ for (int i = 0; i < addresses.count(); ++i) {
+ if (i != 0) tmp += ", ";
+ tmp += addresses.at(i).toString();
+ }
+ qDebug("QHostInfoAgent::run(%p): found %i entries: {%s}",
+ this, addresses.count(), tmp.toLatin1().constData());
+ }
+#endif
+ return results;
+}
+
+QString QHostInfo::localHostName()
+{
+ QWindowsSockInit winSock;
+
+ char hostName[512];
+ if (gethostname(hostName, sizeof(hostName)) == -1)
+ return QString();
+ hostName[sizeof(hostName) - 1] = '\0';
+ return QString::fromLocal8Bit(hostName);
+}
+
+// QString QHostInfo::localDomainName() defined in qnetworkinterface_win.cpp
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
new file mode 100644
index 0000000000..e72bc65c2b
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -0,0 +1,619 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
+
+#include "qdebug.h"
+#include "qendian.h"
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+QT_BEGIN_NAMESPACE
+
+static QList<QNetworkInterfacePrivate *> postProcess(QList<QNetworkInterfacePrivate *> list)
+{
+ // Some platforms report a netmask but don't report a broadcast address
+ // Go through all available addresses and calculate the broadcast address
+ // from the IP and the netmask
+ //
+ // This is an IPv4-only thing -- IPv6 has no concept of broadcasts
+ // The math is:
+ // broadcast = IP | ~netmask
+
+ QList<QNetworkInterfacePrivate *>::Iterator it = list.begin();
+ const QList<QNetworkInterfacePrivate *>::Iterator end = list.end();
+ for ( ; it != end; ++it) {
+ QList<QNetworkAddressEntry>::Iterator addr_it = (*it)->addressEntries.begin();
+ const QList<QNetworkAddressEntry>::Iterator addr_end = (*it)->addressEntries.end();
+ for ( ; addr_it != addr_end; ++addr_it) {
+ if (addr_it->ip().protocol() != QAbstractSocket::IPv4Protocol)
+ continue;
+
+ if (!addr_it->netmask().isNull() && addr_it->broadcast().isNull()) {
+ QHostAddress bcast = addr_it->ip();
+ bcast = QHostAddress(bcast.toIPv4Address() | ~addr_it->netmask().toIPv4Address());
+ addr_it->setBroadcast(bcast);
+ }
+ }
+ }
+
+ return list;
+}
+
+Q_GLOBAL_STATIC(QNetworkInterfaceManager, manager)
+
+QNetworkInterfaceManager::QNetworkInterfaceManager()
+{
+}
+
+QNetworkInterfaceManager::~QNetworkInterfaceManager()
+{
+}
+
+QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interfaceFromName(const QString &name)
+{
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces();
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin();
+ for ( ; it != interfaceList.constEnd(); ++it)
+ if ((*it)->name == name)
+ return *it;
+
+ return empty;
+}
+
+QSharedDataPointer<QNetworkInterfacePrivate> QNetworkInterfaceManager::interfaceFromIndex(int index)
+{
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> > interfaceList = allInterfaces();
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> >::ConstIterator it = interfaceList.constBegin();
+ for ( ; it != interfaceList.constEnd(); ++it)
+ if ((*it)->index == index)
+ return *it;
+
+ return empty;
+}
+
+QList<QSharedDataPointer<QNetworkInterfacePrivate> > QNetworkInterfaceManager::allInterfaces()
+{
+ QList<QNetworkInterfacePrivate *> list = postProcess(scan());
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> > result;
+
+ foreach (QNetworkInterfacePrivate *ptr, list)
+ result << QSharedDataPointer<QNetworkInterfacePrivate>(ptr);
+
+ return result;
+}
+
+QString QNetworkInterfacePrivate::makeHwAddress(int len, uchar *data)
+{
+ QString result;
+ for (int i = 0; i < len; ++i) {
+ if (i)
+ result += QLatin1Char(':');
+
+ char buf[3];
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400
+ sprintf_s(buf, 3, "%02hX", ushort(data[i]));
+#else
+ sprintf(buf, "%02hX", ushort(data[i]));
+#endif
+ result += QLatin1String(buf);
+ }
+ return result;
+}
+
+/*!
+ \class QNetworkAddressEntry
+ \brief The QNetworkAddressEntry class stores one IP address
+ supported by a network interface, along with its associated
+ netmask and broadcast address.
+
+ \since 4.2
+ \reentrant
+ \ingroup network
+
+ Each network interface can contain zero or more IP addresses, which
+ in turn can be associated with a netmask and/or a broadcast
+ address (depending on support from the operating system).
+
+ This class represents one such group.
+*/
+
+/*!
+ Constructs an empty QNetworkAddressEntry object.
+*/
+QNetworkAddressEntry::QNetworkAddressEntry()
+ : d(new QNetworkAddressEntryPrivate)
+{
+}
+
+/*!
+ Constructs a QNetworkAddressEntry object that is a copy of the
+ object \a other.
+*/
+QNetworkAddressEntry::QNetworkAddressEntry(const QNetworkAddressEntry &other)
+ : d(new QNetworkAddressEntryPrivate(*other.d.data()))
+{
+}
+
+/*!
+ Makes a copy of the QNetworkAddressEntry object \a other.
+*/
+QNetworkAddressEntry &QNetworkAddressEntry::operator=(const QNetworkAddressEntry &other)
+{
+ *d.data() = *other.d.data();
+ return *this;
+}
+
+/*!
+ Destroys this QNetworkAddressEntry object.
+*/
+QNetworkAddressEntry::~QNetworkAddressEntry()
+{
+}
+
+/*!
+ Returns true if this network address entry is the same as \a
+ other.
+*/
+bool QNetworkAddressEntry::operator==(const QNetworkAddressEntry &other) const
+{
+ if (d == other.d) return true;
+ if (!d || !other.d) return false;
+ return d->address == other.d->address &&
+ d->netmask == other.d->netmask &&
+ d->broadcast == other.d->broadcast;
+}
+
+/*!
+ \fn bool QNetworkAddressEntry::operator!=(const QNetworkAddressEntry &other) const
+
+ Returns true if this network address entry is different from \a
+ other.
+*/
+
+/*!
+ This function returns one IPv4 or IPv6 address found, that was
+ found in a network interface.
+*/
+QHostAddress QNetworkAddressEntry::ip() const
+{
+ return d->address;
+}
+
+/*!
+ Sets the IP address the QNetworkAddressEntry object contains to \a
+ newIp.
+*/
+void QNetworkAddressEntry::setIp(const QHostAddress &newIp)
+{
+ d->address = newIp;
+}
+
+/*!
+ Returns the netmask associated with the IP address. The
+ netmask is expressed in the form of an IP address, such as
+ 255.255.0.0.
+
+ For IPv6 addresses, the prefix length is converted to an address
+ where the number of bits set to 1 is equal to the prefix
+ length. For a prefix length of 64 bits (the most common value),
+ the netmask will be expressed as a QHostAddress holding the
+ address FFFF:FFFF:FFFF:FFFF::
+
+ \sa prefixLength()
+*/
+QHostAddress QNetworkAddressEntry::netmask() const
+{
+ return d->netmask;
+}
+
+/*!
+ Sets the netmask that this QNetworkAddressEntry object contains to
+ \a newNetmask. Setting the netmask also sets the prefix length to
+ match the new netmask.
+
+ \sa setPrefixLength()
+*/
+void QNetworkAddressEntry::setNetmask(const QHostAddress &newNetmask)
+{
+ if (newNetmask.protocol() != ip().protocol()) {
+ d->netmask = QNetmaskAddress();
+ return;
+ }
+
+ d->netmask.setAddress(newNetmask);
+}
+
+/*!
+ \since 4.5
+ Returns the prefix length of this IP address. The prefix length
+ matches the number of bits set to 1 in the netmask (see
+ netmask()). For IPv4 addresses, the value is between 0 and 32. For
+ IPv6 addresses, it's contained between 0 and 128 and is the
+ preferred form of representing addresses.
+
+ This function returns -1 if the prefix length could not be
+ determined (i.e., netmask() returns a null QHostAddress()).
+
+ \sa netmask()
+*/
+int QNetworkAddressEntry::prefixLength() const
+{
+ return d->netmask.prefixLength();
+}
+
+/*!
+ \since 4.5
+ Sets the prefix length of this IP address to \a length. The value
+ of \a length must be valid for this type of IP address: between 0
+ and 32 for IPv4 addresses, between 0 and 128 for IPv6
+ addresses. Setting to any invalid value is equivalent to setting
+ to -1, which means "no prefix length".
+
+ Setting the prefix length also sets the netmask (see netmask()).
+
+ \sa setNetmask()
+*/
+void QNetworkAddressEntry::setPrefixLength(int length)
+{
+ d->netmask.setPrefixLength(d->address.protocol(), length);
+}
+
+/*!
+ Returns the broadcast address associated with the IPv4
+ address and netmask. It can usually be derived from those two by
+ setting to 1 the bits of the IP address where the netmask contains
+ a 0. (In other words, by bitwise-OR'ing the IP address with the
+ inverse of the netmask)
+
+ This member is always empty for IPv6 addresses, since the concept
+ of broadcast has been abandoned in that system in favor of
+ multicast. In particular, the group of hosts corresponding to all
+ the nodes in the local network can be reached by the "all-nodes"
+ special multicast group (address FF02::1).
+*/
+QHostAddress QNetworkAddressEntry::broadcast() const
+{
+ return d->broadcast;
+}
+
+/*!
+ Sets the broadcast IP address of this QNetworkAddressEntry object
+ to \a newBroadcast.
+*/
+void QNetworkAddressEntry::setBroadcast(const QHostAddress &newBroadcast)
+{
+ d->broadcast = newBroadcast;
+}
+
+/*!
+ \class QNetworkInterface
+ \brief The QNetworkInterface class provides a listing of the host's IP
+ addresses and network interfaces.
+
+ \since 4.2
+ \reentrant
+ \ingroup network
+
+ QNetworkInterface represents one network interface attached to the
+ host where the program is being run. Each network interface may
+ contain zero or more IP addresses, each of which is optionally
+ associated with a netmask and/or a broadcast address. The list of
+ such trios can be obtained with addressEntries(). Alternatively,
+ when the netmask or the broadcast addresses aren't necessary, use
+ the allAddresses() convenience function to obtain just the IP
+ addresses.
+
+ QNetworkInterface also reports the interface's hardware address with
+ hardwareAddress().
+
+ Not all operating systems support reporting all features. Only the
+ IPv4 addresses are guaranteed to be listed by this class in all
+ platforms. In particular, IPv6 address listing is only supported
+ on Windows XP and more recent versions, Linux, MacOS X and the
+ BSDs.
+
+ \sa QNetworkAddressEntry
+*/
+
+/*!
+ \enum QNetworkInterface::InterfaceFlag
+ Specifies the flags associated with this network interface. The
+ possible values are:
+
+ \value IsUp the network interface is active
+ \value IsRunning the network interface has resources
+ allocated
+ \value CanBroadcast the network interface works in
+ broadcast mode
+ \value IsLoopBack the network interface is a loopback
+ interface: that is, it's a virtual
+ interface whose destination is the
+ host computer itself
+ \value IsPointToPoint the network interface is a
+ point-to-point interface: that is,
+ there is one, single other address
+ that can be directly reached by it.
+ \value CanMulticast the network interface supports
+ multicasting
+
+ Note that one network interface cannot be both broadcast-based and
+ point-to-point.
+*/
+
+/*!
+ Constructs an empty network interface object.
+*/
+QNetworkInterface::QNetworkInterface()
+ : d(0)
+{
+}
+
+/*!
+ Frees the resources associated with the QNetworkInterface object.
+*/
+QNetworkInterface::~QNetworkInterface()
+{
+}
+
+/*!
+ Creates a copy of the QNetworkInterface object contained in \a
+ other.
+*/
+QNetworkInterface::QNetworkInterface(const QNetworkInterface &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Copies the contents of the QNetworkInterface object contained in \a
+ other into this one.
+*/
+QNetworkInterface &QNetworkInterface::operator=(const QNetworkInterface &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this QNetworkInterface object contains valid
+ information about a network interface.
+*/
+bool QNetworkInterface::isValid() const
+{
+ return !name().isEmpty();
+}
+
+/*!
+ \since 4.5
+ Returns the interface system index, if known. This is an integer
+ assigned by the operating system to identify this interface and it
+ generally doesn't change. It matches the scope ID field in IPv6
+ addresses.
+
+ If the index isn't known, this function returns 0.
+*/
+int QNetworkInterface::index() const
+{
+ return d ? d->index : 0;
+}
+
+/*!
+ Returns the name of this network interface. On Unix systems, this
+ is a string containing the type of the interface and optionally a
+ sequence number, such as "eth0", "lo" or "pcn0". On Windows, it's
+ an internal ID that cannot be changed by the user.
+*/
+QString QNetworkInterface::name() const
+{
+ return d ? d->name : QString();
+}
+
+/*!
+ \since 4.5
+
+ Returns the human-readable name of this network interface on
+ Windows, such as "Local Area Connection", if the name could be
+ determined. If it couldn't, this function returns the same as
+ name(). The human-readable name is a name that the user can modify
+ in the Windows Control Panel, so it may change during the
+ execution of the program.
+
+ On Unix, this function currently always returns the same as
+ name(), since Unix systems don't store a configuration for
+ human-readable names.
+*/
+QString QNetworkInterface::humanReadableName() const
+{
+ return d ? !d->friendlyName.isEmpty() ? d->friendlyName : name() : QString();
+}
+
+/*!
+ Returns the flags associated with this network interface.
+*/
+QNetworkInterface::InterfaceFlags QNetworkInterface::flags() const
+{
+ return d ? d->flags : InterfaceFlags(0);
+}
+
+/*!
+ Returns the low-level hardware address for this interface. On
+ Ethernet interfaces, this will be a MAC address in string
+ representation, separated by colons.
+
+ Other interface types may have other types of hardware
+ addresses. Implementations should not depend on this function
+ returning a valid MAC address.
+*/
+QString QNetworkInterface::hardwareAddress() const
+{
+ return d ? d->hardwareAddress : QString();
+}
+
+/*!
+ Returns the list of IP addresses that this interface possesses
+ along with their associated netmasks and broadcast addresses.
+
+ If the netmask or broadcast address information is not necessary,
+ you can call the allAddresses() function to obtain just the IP
+ addresses.
+*/
+QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const
+{
+ return d ? d->addressEntries : QList<QNetworkAddressEntry>();
+}
+
+/*!
+ Returns a QNetworkInterface object for the interface named \a
+ name. If no such interface exists, this function returns an
+ invalid QNetworkInterface object.
+
+ \sa name(), isValid()
+*/
+QNetworkInterface QNetworkInterface::interfaceFromName(const QString &name)
+{
+ QNetworkInterface result;
+ result.d = manager()->interfaceFromName(name);
+ return result;
+}
+
+/*!
+ Returns a QNetworkInterface object for the interface whose internal
+ ID is \a index. Network interfaces have a unique identifier called
+ the "interface index" to distinguish it from other interfaces on
+ the system. Often, this value is assigned progressively and
+ interfaces being removed and then added again get a different
+ value every time.
+
+ This index is also found in the IPv6 address' scope ID field.
+*/
+QNetworkInterface QNetworkInterface::interfaceFromIndex(int index)
+{
+ QNetworkInterface result;
+ result.d = manager()->interfaceFromIndex(index);
+ return result;
+}
+
+/*!
+ Returns a listing of all the network interfaces found on the host
+ machine.
+*/
+QList<QNetworkInterface> QNetworkInterface::allInterfaces()
+{
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> > privs = manager()->allInterfaces();
+ QList<QNetworkInterface> result;
+ foreach (const QSharedDataPointer<QNetworkInterfacePrivate> &p, privs) {
+ QNetworkInterface item;
+ item.d = p;
+ result << item;
+ }
+
+ return result;
+}
+
+/*!
+ This convenience function returns all IP addresses found on the
+ host machine. It is equivalent to calling addressEntries() on all the
+ objects returned by allInterfaces() to obtain lists of QHostAddress
+ objects then calling QHostAddress::ip() on each of these.
+*/
+QList<QHostAddress> QNetworkInterface::allAddresses()
+{
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> > privs = manager()->allInterfaces();
+ QList<QHostAddress> result;
+ foreach (const QSharedDataPointer<QNetworkInterfacePrivate> &p, privs) {
+ foreach (const QNetworkAddressEntry &entry, p->addressEntries)
+ result += entry.ip();
+ }
+
+ return result;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+static inline QDebug flagsDebug(QDebug debug, QNetworkInterface::InterfaceFlags flags)
+{
+ if (flags & QNetworkInterface::IsUp)
+ debug.nospace() << "IsUp ";
+ if (flags & QNetworkInterface::IsRunning)
+ debug.nospace() << "IsRunning ";
+ if (flags & QNetworkInterface::CanBroadcast)
+ debug.nospace() << "CanBroadcast ";
+ if (flags & QNetworkInterface::IsLoopBack)
+ debug.nospace() << "IsLoopBack ";
+ if (flags & QNetworkInterface::IsPointToPoint)
+ debug.nospace() << "IsPointToPoint ";
+ if (flags & QNetworkInterface::CanMulticast)
+ debug.nospace() << "CanMulticast ";
+ return debug.nospace();
+}
+
+static inline QDebug operator<<(QDebug debug, const QNetworkAddressEntry &entry)
+{
+ debug.nospace() << "(address = " << entry.ip();
+ if (!entry.netmask().isNull())
+ debug.nospace() << ", netmask = " << entry.netmask();
+ if (!entry.broadcast().isNull())
+ debug.nospace() << ", broadcast = " << entry.broadcast();
+ debug.nospace() << ')';
+ return debug.space();
+}
+
+QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface)
+{
+ debug.nospace() << "QNetworkInterface(name = " << networkInterface.name()
+ << ", hardware address = " << networkInterface.hardwareAddress()
+ << ", flags = ";
+ flagsDebug(debug, networkInterface.flags());
+#if defined(Q_CC_RVCT)
+ // RVCT gets confused with << networkInterface.addressEntries(), reason unknown.
+ debug.nospace() << ")\n";
+#else
+ debug.nospace() << ", entries = " << networkInterface.addressEntries()
+ << ")\n";
+#endif
+ return debug.space();
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h
new file mode 100644
index 0000000000..d65a6d70a5
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKINTERFACE_H
+#define QNETWORKINTERFACE_H
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qhostaddress.h>
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+template<typename T> class QList;
+
+class QNetworkAddressEntryPrivate;
+class Q_NETWORK_EXPORT QNetworkAddressEntry
+{
+public:
+ QNetworkAddressEntry();
+ QNetworkAddressEntry(const QNetworkAddressEntry &other);
+ QNetworkAddressEntry &operator=(const QNetworkAddressEntry &other);
+ ~QNetworkAddressEntry();
+ bool operator==(const QNetworkAddressEntry &other) const;
+ inline bool operator!=(const QNetworkAddressEntry &other) const
+ { return !(*this == other); }
+
+ QHostAddress ip() const;
+ void setIp(const QHostAddress &newIp);
+
+ QHostAddress netmask() const;
+ void setNetmask(const QHostAddress &newNetmask);
+ int prefixLength() const;
+ void setPrefixLength(int length);
+
+ QHostAddress broadcast() const;
+ void setBroadcast(const QHostAddress &newBroadcast);
+
+private:
+ QScopedPointer<QNetworkAddressEntryPrivate> d;
+};
+
+class QNetworkInterfacePrivate;
+class Q_NETWORK_EXPORT QNetworkInterface
+{
+public:
+ enum InterfaceFlag {
+ IsUp = 0x1,
+ IsRunning = 0x2,
+ CanBroadcast = 0x4,
+ IsLoopBack = 0x8,
+ IsPointToPoint = 0x10,
+ CanMulticast = 0x20
+ };
+ Q_DECLARE_FLAGS(InterfaceFlags, InterfaceFlag)
+
+ QNetworkInterface();
+ QNetworkInterface(const QNetworkInterface &other);
+ QNetworkInterface &operator=(const QNetworkInterface &other);
+ ~QNetworkInterface();
+
+ bool isValid() const;
+
+ int index() const;
+ QString name() const;
+ QString humanReadableName() const;
+ InterfaceFlags flags() const;
+ QString hardwareAddress() const;
+ QList<QNetworkAddressEntry> addressEntries() const;
+
+ static QNetworkInterface interfaceFromName(const QString &name);
+ static QNetworkInterface interfaceFromIndex(int index);
+ static QList<QNetworkInterface> allInterfaces();
+ static QList<QHostAddress> allAddresses();
+
+private:
+ friend class QNetworkInterfacePrivate;
+ QSharedDataPointer<QNetworkInterfacePrivate> d;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkInterface::InterfaceFlags)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkInterface &networkInterface);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_NETWORKINTERFACE
+
+#endif
diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h
new file mode 100644
index 0000000000..0136593b8a
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKINTERFACEPRIVATE_H
+#define QNETWORKINTERFACEPRIVATE_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 <QtCore/qatomic.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qreadwritelock.h>
+#include <QtCore/qstring.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qabstractsocket.h>
+#include <private/qhostaddress_p.h>
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAddressEntryPrivate
+{
+public:
+ QHostAddress address;
+ QNetmaskAddress netmask;
+ QHostAddress broadcast;
+};
+
+class QNetworkInterfacePrivate: public QSharedData
+{
+public:
+ QNetworkInterfacePrivate() : index(0), flags(0)
+ { }
+ ~QNetworkInterfacePrivate()
+ { }
+
+ int index; // interface index, if know
+ QNetworkInterface::InterfaceFlags flags;
+
+ QString name;
+ QString friendlyName;
+ QString hardwareAddress;
+
+ QList<QNetworkAddressEntry> addressEntries;
+
+ static QString makeHwAddress(int len, uchar *data);
+
+private:
+ // disallow copying -- avoid detaching
+ QNetworkInterfacePrivate &operator=(const QNetworkInterfacePrivate &other);
+ QNetworkInterfacePrivate(const QNetworkInterfacePrivate &other);
+};
+
+class QNetworkInterfaceManager
+{
+public:
+ QNetworkInterfaceManager();
+ ~QNetworkInterfaceManager();
+
+ QSharedDataPointer<QNetworkInterfacePrivate> interfaceFromName(const QString &name);
+ QSharedDataPointer<QNetworkInterfacePrivate> interfaceFromIndex(int index);
+ QList<QSharedDataPointer<QNetworkInterfacePrivate> > allInterfaces();
+
+ // convenience:
+ QSharedDataPointer<QNetworkInterfacePrivate> empty;
+
+private:
+ QList<QNetworkInterfacePrivate *> scan();
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
+
+#endif
diff --git a/src/network/kernel/qnetworkinterface_symbian.cpp b/src/network/kernel/qnetworkinterface_symbian.cpp
new file mode 100644
index 0000000000..7767f54085
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface_symbian.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** 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 of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNETWORKINTERFACE_DEBUG
+
+#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
+#include <private/qcore_symbian_p.h>
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+#include <in_sock.h>
+#include <in_iface.h>
+#include <es_sock.h>
+
+QT_BEGIN_NAMESPACE
+
+
+static QNetworkInterface::InterfaceFlags convertFlags(const TSoInetInterfaceInfo& aInfo)
+{
+ QNetworkInterface::InterfaceFlags flags = 0;
+ flags |= (aInfo.iState == EIfUp) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0);
+ // We do not have separate flag for running in Symbian OS
+ flags |= (aInfo.iState == EIfUp) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0);
+ flags |= (aInfo.iFeatures & KIfCanBroadcast) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0);
+ flags |= (aInfo.iFeatures & KIfIsLoopback) ? QNetworkInterface::IsLoopBack : QNetworkInterface::InterfaceFlag(0);
+ flags |= (aInfo.iFeatures & KIfIsPointToPoint) ? QNetworkInterface::IsPointToPoint : QNetworkInterface::InterfaceFlag(0);
+ flags |= (aInfo.iFeatures & KIfCanMulticast) ? QNetworkInterface::CanMulticast : QNetworkInterface::InterfaceFlag(0);
+ return flags;
+}
+
+//TODO: share this, at least QHostInfo needs to do the same thing
+static QHostAddress qt_QHostAddressFromTInetAddr(const TInetAddr& addr)
+{
+ //TODO: do we want to call v4 mapped addresses v4 or v6 outside of this file?
+ 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;
+ QList<QHostAddress> addressesWithEstimatedNetmasks;
+
+ // Open dummy socket for interface queries
+ RSocket socket;
+ err = socket.Open(qt_symbianGetSocketServer(), _L("udp"));
+ if (err) {
+ return interfaces;
+ }
+
+ // Ask socket to start enumerating interfaces
+ err = socket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl);
+ if (err) {
+ socket.Close();
+ return interfaces;
+ }
+
+ int ifindex = 0;
+ TPckgBuf<TSoInetInterfaceInfo> infoPckg;
+ TSoInetInterfaceInfo &info = infoPckg();
+ while (socket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, infoPckg) == KErrNone) {
+ if (info.iName != KNullDesC) {
+ TName address;
+ QNetworkAddressEntry entry;
+ QNetworkInterfacePrivate *iface = 0;
+
+ iface = new QNetworkInterfacePrivate;
+ iface->index = ifindex++;
+ interfaces << iface;
+ iface->name = qt_TDesC2QString(info.iName);
+ iface->flags = convertFlags(info);
+
+ if (/*info.iFeatures&KIfHasHardwareAddr &&*/ info.iHwAddr.Family() != KAFUnspec) {
+ for (TInt i = sizeof(SSockAddr); i < sizeof(SSockAddr) + info.iHwAddr.GetUserLen(); i++) {
+ address.AppendNumFixedWidth(info.iHwAddr[i], EHex, 2);
+ if ((i + 1) < sizeof(SSockAddr) + info.iHwAddr.GetUserLen())
+ address.Append(_L(":"));
+ }
+ address.UpperCase();
+ iface->hardwareAddress = qt_TDesC2QString(address);
+ }
+
+ // Get the address of the interface
+ 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
+ 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)
+ 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\
+ hwaddress = %s",
+ iface->name.toLatin1().constData(),
+ iface->flags & QNetworkInterface::IsUp, iface->flags & QNetworkInterface::IsRunning, iface->flags & QNetworkInterface::CanBroadcast,
+ iface->flags & QNetworkInterface::IsLoopBack, iface->flags & QNetworkInterface::IsPointToPoint, iface->flags & QNetworkInterface::CanMulticast,
+ entry.ip().toString().toLatin1().constData(), entry.netmask().toString().toLatin1().constData(), entry.broadcast().toString().toLatin1().constData(),
+ iface->hardwareAddress.toLatin1().constData());
+#endif
+ }
+ }
+
+ // 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
+ // 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();
+ // return what we have
+ // up to this moment
+ return interfaces;
+ }
+
+ TSoInetRouteInfo routeInfo;
+ TPckg<TSoInetRouteInfo> routeInfoPkg(routeInfo);
+ while (socket.GetOpt(KSoInetNextRoute, KSolInetRtCtrl, routeInfoPkg) == KErrNone) {
+ // get interface 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;
+ }
+
+ QHostAddress destination(qt_QHostAddressFromTInetAddr(routeInfo.iDstAddr));
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << "route from" << ifAddr << "to" << destination;
+#endif
+ if (destination.isNull() || destination != ifAddr)
+ continue;
+
+ // search interfaces
+ for (int ifindex = 0; ifindex < interfaces.size(); ++ifindex) {
+ QNetworkInterfacePrivate *iface = interfaces.at(ifindex);
+ for (int eindex = 0; eindex < iface->addressEntries.size(); ++eindex) {
+ QNetworkAddressEntry entry = iface->addressEntries.at(eindex);
+ if (entry.ip() != ifAddr) {
+ continue;
+ } 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);
+#if defined(QNETWORKINTERFACE_DEBUG)
+ qDebug() << " - route netmask" << routeInfo.iNetMask.Family() << netmask << " (using route determined netmask)";
+#endif
+ iface->addressEntries.replace(eindex, entry);
+ }
+ }
+ }
+ }
+
+ socket.Close();
+
+ return interfaces;
+}
+
+QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
+{
+ return interfaceListing();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
new file mode 100644
index 0000000000..6098bdeee0
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -0,0 +1,449 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qset.h"
+#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
+#include "qalgorithms.h"
+#include "private/qnet_unix_p.h"
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+#define IP_MULTICAST // make AIX happy and define IFF_MULTICAST
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef Q_OS_SOLARIS
+# include <sys/sockio.h>
+#endif
+#include <net/if.h>
+
+#ifndef QT_NO_GETIFADDRS
+# include <ifaddrs.h>
+#endif
+
+#ifdef QT_LINUXBASE
+# include <arpa/inet.h>
+# ifndef SIOCGIFBRDADDR
+# define SIOCGIFBRDADDR 0x8919
+# endif
+#endif // QT_LINUXBASE
+
+#include <qplatformdefs.h>
+
+QT_BEGIN_NAMESPACE
+
+static QHostAddress addressFromSockaddr(sockaddr *sa)
+{
+ QHostAddress address;
+ if (!sa)
+ return address;
+
+ if (sa->sa_family == AF_INET)
+ address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr));
+#ifndef QT_NO_IPV6
+ else if (sa->sa_family == AF_INET6)
+ address.setAddress(((sockaddr_in6 *)sa)->sin6_addr.s6_addr);
+#endif
+ return address;
+
+}
+
+static QNetworkInterface::InterfaceFlags convertFlags(uint rawFlags)
+{
+ QNetworkInterface::InterfaceFlags flags = 0;
+ flags |= (rawFlags & IFF_UP) ? QNetworkInterface::IsUp : QNetworkInterface::InterfaceFlag(0);
+ flags |= (rawFlags & IFF_RUNNING) ? QNetworkInterface::IsRunning : QNetworkInterface::InterfaceFlag(0);
+ flags |= (rawFlags & IFF_BROADCAST) ? QNetworkInterface::CanBroadcast : QNetworkInterface::InterfaceFlag(0);
+ flags |= (rawFlags & IFF_LOOPBACK) ? QNetworkInterface::IsLoopBack : QNetworkInterface::InterfaceFlag(0);
+#ifdef IFF_POINTOPOINT //cygwin doesn't define IFF_POINTOPOINT
+ flags |= (rawFlags & IFF_POINTOPOINT) ? QNetworkInterface::IsPointToPoint : QNetworkInterface::InterfaceFlag(0);
+#endif
+
+#ifdef IFF_MULTICAST
+ flags |= (rawFlags & IFF_MULTICAST) ? QNetworkInterface::CanMulticast : QNetworkInterface::InterfaceFlag(0);
+#endif
+ return flags;
+}
+
+#ifdef QT_NO_GETIFADDRS
+// getifaddrs not available
+
+static const int STORAGEBUFFER_GROWTH = 256;
+
+static QSet<QByteArray> interfaceNames(int socket)
+{
+ QSet<QByteArray> result;
+#ifdef QT_NO_IPV6IFNAME
+ QByteArray storageBuffer;
+ struct ifconf interfaceList;
+
+ forever {
+ // grow the storage buffer
+ storageBuffer.resize(storageBuffer.size() + STORAGEBUFFER_GROWTH);
+ interfaceList.ifc_buf = storageBuffer.data();
+ interfaceList.ifc_len = storageBuffer.size();
+
+ // get the interface list
+ if (qt_safe_ioctl(socket, SIOCGIFCONF, &interfaceList) >= 0) {
+ if (int(interfaceList.ifc_len + sizeof(ifreq) + 64) < storageBuffer.size()) {
+ // if the buffer was big enough, break
+ storageBuffer.resize(interfaceList.ifc_len);
+ break;
+ }
+ } else {
+ // internal error
+ return result;
+ }
+ if (storageBuffer.size() > 100000) {
+ // out of space
+ return result;
+ }
+ }
+
+ int interfaceCount = interfaceList.ifc_len / sizeof(ifreq);
+ for (int i = 0; i < interfaceCount; ++i) {
+ QByteArray name = QByteArray(interfaceList.ifc_req[i].ifr_name);
+ if (!name.isEmpty())
+ result << name;
+ }
+
+ return result;
+#else
+ Q_UNUSED(socket);
+
+ // use if_nameindex
+ struct if_nameindex *interfaceList = ::if_nameindex();
+ for (struct if_nameindex *ptr = interfaceList; ptr && ptr->if_name; ++ptr)
+ result << ptr->if_name;
+
+ if_freenameindex(interfaceList);
+ return result;
+#endif
+}
+
+static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfacePrivate *> &interfaces,
+ struct ifreq &req)
+{
+ QNetworkInterfacePrivate *iface = 0;
+ int ifindex = 0;
+
+#ifndef QT_NO_IPV6IFNAME
+ // Get the interface index
+ ifindex = if_nametoindex(req.ifr_name);
+
+ // find the interface data
+ QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
+ for ( ; if_it != interfaces.end(); ++if_it)
+ if ((*if_it)->index == ifindex) {
+ // existing interface
+ iface = *if_it;
+ break;
+ }
+#else
+ // Search by name
+ QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
+ for ( ; if_it != interfaces.end(); ++if_it)
+ if ((*if_it)->name == QLatin1String(req.ifr_name)) {
+ // existing interface
+ iface = *if_it;
+ break;
+ }
+#endif
+
+ if (!iface) {
+ // new interface, create data:
+ iface = new QNetworkInterfacePrivate;
+ iface->index = ifindex;
+ interfaces << iface;
+
+#ifdef SIOCGIFNAME
+ // Get the canonical name
+ QByteArray oldName = req.ifr_name;
+ if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
+ iface->name = QString::fromLatin1(req.ifr_name);
+
+ // reset the name:
+ memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1));
+ } else
+#endif
+ {
+ // use this name anyways
+ iface->name = QString::fromLatin1(req.ifr_name);
+ }
+
+ // Get interface flags
+ if (qt_safe_ioctl(socket, SIOCGIFFLAGS, &req) >= 0) {
+ iface->flags = convertFlags(req.ifr_flags);
+ }
+
+#ifdef SIOCGIFHWADDR
+ // Get the HW address
+ if (qt_safe_ioctl(socket, SIOCGIFHWADDR, &req) >= 0) {
+ uchar *addr = (uchar *)&req.ifr_addr;
+ iface->hardwareAddress = iface->makeHwAddress(6, addr);
+ }
+#endif
+ }
+
+ return iface;
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListing()
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+
+ int socket;
+ if ((socket = qt_safe_socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1)
+ return interfaces; // error
+
+ QSet<QByteArray> names = interfaceNames(socket);
+ QSet<QByteArray>::ConstIterator it = names.constBegin();
+ for ( ; it != names.constEnd(); ++it) {
+ ifreq req;
+ memset(&req, 0, sizeof(ifreq));
+ memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1));
+
+ QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req);
+
+ // Get the interface broadcast address
+ QNetworkAddressEntry entry;
+ if (iface->flags & QNetworkInterface::CanBroadcast) {
+ if (qt_safe_ioctl(socket, SIOCGIFBRDADDR, &req) >= 0) {
+ sockaddr *sa = &req.ifr_addr;
+ if (sa->sa_family == AF_INET)
+ entry.setBroadcast(addressFromSockaddr(sa));
+ }
+ }
+
+ // Get the interface netmask
+ if (qt_safe_ioctl(socket, SIOCGIFNETMASK, &req) >= 0) {
+ sockaddr *sa = &req.ifr_addr;
+ entry.setNetmask(addressFromSockaddr(sa));
+ }
+
+ // Get the address of the interface
+ if (qt_safe_ioctl(socket, SIOCGIFADDR, &req) >= 0) {
+ sockaddr *sa = &req.ifr_addr;
+ entry.setIp(addressFromSockaddr(sa));
+ }
+
+ iface->addressEntries << entry;
+ }
+
+ ::close(socket);
+ return interfaces;
+}
+
+#else
+// use getifaddrs
+
+// platform-specific defs:
+# ifdef Q_OS_LINUX
+QT_BEGIN_INCLUDE_NAMESPACE
+# include <features.h>
+QT_END_INCLUDE_NAMESPACE
+# endif
+
+# if defined(Q_OS_LINUX) && __GLIBC__ - 0 >= 2 && __GLIBC_MINOR__ - 0 >= 1
+# include <netpacket/packet.h>
+
+static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+
+ for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
+ if ( !ptr->ifa_addr )
+ continue;
+
+ // Get the interface index
+ int ifindex = if_nametoindex(ptr->ifa_name);
+
+ // on Linux we use AF_PACKET and sockaddr_ll to obtain hHwAddress
+ QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
+ for ( ; if_it != interfaces.end(); ++if_it)
+ if ((*if_it)->index == ifindex) {
+ // this one has been added already
+ if ( ptr->ifa_addr->sa_family == AF_PACKET
+ && (*if_it)->hardwareAddress.isEmpty()) {
+ sockaddr_ll *sll = (sockaddr_ll *)ptr->ifa_addr;
+ (*if_it)->hardwareAddress = (*if_it)->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr);
+ }
+ break;
+ }
+ if ( if_it != interfaces.end() )
+ continue;
+
+ QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+ interfaces << iface;
+ iface->index = ifindex;
+ iface->name = QString::fromLatin1(ptr->ifa_name);
+ iface->flags = convertFlags(ptr->ifa_flags);
+
+ if ( ptr->ifa_addr->sa_family == AF_PACKET ) {
+ sockaddr_ll *sll = (sockaddr_ll *)ptr->ifa_addr;
+ iface->hardwareAddress = iface->makeHwAddress(sll->sll_halen, (uchar*)sll->sll_addr);
+ }
+ }
+
+ return interfaces;
+}
+
+# elif defined(Q_OS_BSD4)
+QT_BEGIN_INCLUDE_NAMESPACE
+# include <net/if_dl.h>
+QT_END_INCLUDE_NAMESPACE
+
+static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+
+ // on NetBSD we use AF_LINK and sockaddr_dl
+ // scan the list for that family
+ for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next)
+ if (ptr->ifa_addr && ptr->ifa_addr->sa_family == AF_LINK) {
+ QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+ interfaces << iface;
+
+ sockaddr_dl *sdl = (sockaddr_dl *)ptr->ifa_addr;
+ iface->index = sdl->sdl_index;
+ iface->name = QString::fromLatin1(ptr->ifa_name);
+ iface->flags = convertFlags(ptr->ifa_flags);
+ iface->hardwareAddress = iface->makeHwAddress(sdl->sdl_alen, (uchar*)LLADDR(sdl));
+ }
+
+ return interfaces;
+}
+
+# else // Generic version
+
+static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+
+ // make sure there's one entry for each interface
+ for (ifaddrs *ptr = rawList; ptr; ptr = ptr->ifa_next) {
+ // Get the interface index
+ int ifindex = if_nametoindex(ptr->ifa_name);
+
+ QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
+ for ( ; if_it != interfaces.end(); ++if_it)
+ if ((*if_it)->index == ifindex)
+ // this one has been added already
+ break;
+
+ if (if_it == interfaces.end()) {
+ // none found, create
+ QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+ interfaces << iface;
+
+ iface->index = ifindex;
+ iface->name = QString::fromLatin1(ptr->ifa_name);
+ iface->flags = convertFlags(ptr->ifa_flags);
+ }
+ }
+
+ return interfaces;
+}
+
+# endif
+
+
+static QList<QNetworkInterfacePrivate *> interfaceListing()
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+
+ int socket;
+ if ((socket = qt_safe_socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1)
+ return interfaces; // error
+
+ ifaddrs *interfaceListing;
+ if (getifaddrs(&interfaceListing) == -1) {
+ // error
+ ::close(socket);
+ return interfaces;
+ }
+
+ interfaces = createInterfaces(interfaceListing);
+ for (ifaddrs *ptr = interfaceListing; ptr; ptr = ptr->ifa_next) {
+ // Get the interface index
+ int ifindex = if_nametoindex(ptr->ifa_name);
+ QNetworkInterfacePrivate *iface = 0;
+ QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
+ for ( ; if_it != interfaces.end(); ++if_it)
+ if ((*if_it)->index == ifindex) {
+ // found this interface already
+ iface = *if_it;
+ break;
+ }
+ if (!iface) {
+ // skip all non-IP interfaces
+ continue;
+ }
+
+ QNetworkAddressEntry entry;
+ entry.setIp(addressFromSockaddr(ptr->ifa_addr));
+ if (entry.ip().isNull())
+ // could not parse the address
+ continue;
+
+ entry.setNetmask(addressFromSockaddr(ptr->ifa_netmask));
+ if (iface->flags & QNetworkInterface::CanBroadcast)
+ entry.setBroadcast(addressFromSockaddr(ptr->ifa_broadaddr));
+
+ iface->addressEntries << entry;
+ }
+
+ freeifaddrs(interfaceListing);
+ ::close(socket);
+ return interfaces;
+}
+#endif
+
+QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
+{
+ return interfaceListing();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp
new file mode 100644
index 0000000000..e83324c81f
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface_win.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkinterface.h"
+#include "qnetworkinterface_p.h"
+
+#ifndef QT_NO_NETWORKINTERFACE
+
+#include "qnetworkinterface_win_p.h"
+#include <qhostinfo.h>
+#include <qhash.h>
+#include <qurl.h>
+#include <private/qsystemlibrary_p.h>
+
+QT_BEGIN_NAMESPACE
+
+typedef DWORD (WINAPI *PtrGetAdaptersInfo)(PIP_ADAPTER_INFO, PULONG);
+static PtrGetAdaptersInfo ptrGetAdaptersInfo = 0;
+typedef ULONG (WINAPI *PtrGetAdaptersAddresses)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG);
+static PtrGetAdaptersAddresses ptrGetAdaptersAddresses = 0;
+typedef DWORD (WINAPI *PtrGetNetworkParams)(PFIXED_INFO, PULONG);
+static PtrGetNetworkParams ptrGetNetworkParams = 0;
+
+static void resolveLibs()
+{
+ // try to find the functions we need from Iphlpapi.dll
+ static bool done = false;
+
+ if (!done) {
+ done = true;
+
+ HINSTANCE iphlpapiHnd = QSystemLibrary::load(L"iphlpapi");
+ if (iphlpapiHnd == NULL)
+ return;
+
+#if defined(Q_OS_WINCE)
+ ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, L"GetAdaptersInfo");
+ ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, L"GetAdaptersAddresses");
+ ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, L"GetNetworkParams");
+#else
+ ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddress(iphlpapiHnd, "GetAdaptersInfo");
+ ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddress(iphlpapiHnd, "GetAdaptersAddresses");
+ ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddress(iphlpapiHnd, "GetNetworkParams");
+#endif
+ }
+}
+
+static QHostAddress addressFromSockaddr(sockaddr *sa)
+{
+ QHostAddress address;
+ if (!sa)
+ return address;
+
+ if (sa->sa_family == AF_INET)
+ address.setAddress(htonl(((sockaddr_in *)sa)->sin_addr.s_addr));
+ else if (sa->sa_family == AF_INET6)
+ address.setAddress(((qt_sockaddr_in6 *)sa)->sin6_addr.qt_s6_addr);
+ else
+ qWarning("Got unknown socket family %d", sa->sa_family);
+ return address;
+
+}
+
+static QHash<QHostAddress, QHostAddress> ipv4Netmasks()
+{
+ //Retrieve all the IPV4 addresses & netmasks
+ IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary
+ PIP_ADAPTER_INFO pAdapter = staticBuf;
+ ULONG bufSize = sizeof staticBuf;
+ QHash<QHostAddress, QHostAddress> ipv4netmasks;
+
+ DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize);
+ if (retval == ERROR_BUFFER_OVERFLOW) {
+ // need more memory
+ pAdapter = (IP_ADAPTER_INFO *)qMalloc(bufSize);
+ if (!pAdapter)
+ return ipv4netmasks;
+ // try again
+ if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) {
+ qFree(pAdapter);
+ return ipv4netmasks;
+ }
+ } else if (retval != ERROR_SUCCESS) {
+ // error
+ return ipv4netmasks;
+ }
+
+ // iterate over the list and add the entries to our listing
+ for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) {
+ for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) {
+ QHostAddress address(QLatin1String(addr->IpAddress.String));
+ QHostAddress mask(QLatin1String(addr->IpMask.String));
+ ipv4netmasks[address] = mask;
+ }
+ }
+ if (pAdapter != staticBuf)
+ qFree(pAdapter);
+
+ return ipv4netmasks;
+
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListingWinXP()
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+ IP_ADAPTER_ADDRESSES staticBuf[2]; // 2 is arbitrary
+ PIP_ADAPTER_ADDRESSES pAdapter = staticBuf;
+ ULONG bufSize = sizeof staticBuf;
+
+ const QHash<QHostAddress, QHostAddress> &ipv4netmasks = ipv4Netmasks();
+ ULONG flags = GAA_FLAG_INCLUDE_ALL_INTERFACES |
+ GAA_FLAG_INCLUDE_PREFIX |
+ GAA_FLAG_SKIP_DNS_SERVER |
+ GAA_FLAG_SKIP_MULTICAST;
+ ULONG retval = ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize);
+ if (retval == ERROR_BUFFER_OVERFLOW) {
+ // need more memory
+ pAdapter = (IP_ADAPTER_ADDRESSES *)qMalloc(bufSize);
+ if (!pAdapter)
+ return interfaces;
+ // try again
+ if (ptrGetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAdapter, &bufSize) != ERROR_SUCCESS) {
+ qFree(pAdapter);
+ return interfaces;
+ }
+ } else if (retval != ERROR_SUCCESS) {
+ // error
+ return interfaces;
+ }
+
+ // iterate over the list and add the entries to our listing
+ for (PIP_ADAPTER_ADDRESSES ptr = pAdapter; ptr; ptr = ptr->Next) {
+ QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+ interfaces << iface;
+
+ iface->index = 0;
+ if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, Ipv6IfIndex) && ptr->Ipv6IfIndex != 0)
+ iface->index = ptr->Ipv6IfIndex;
+ else if (ptr->IfIndex != 0)
+ iface->index = ptr->IfIndex;
+
+ iface->flags = QNetworkInterface::CanBroadcast;
+ if (ptr->OperStatus == IfOperStatusUp)
+ iface->flags |= QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
+ if ((ptr->Flags & IP_ADAPTER_NO_MULTICAST) == 0)
+ iface->flags |= QNetworkInterface::CanMulticast;
+
+ iface->name = QString::fromLocal8Bit(ptr->AdapterName);
+ iface->friendlyName = QString::fromWCharArray(ptr->FriendlyName);
+ if (ptr->PhysicalAddressLength)
+ iface->hardwareAddress = iface->makeHwAddress(ptr->PhysicalAddressLength,
+ ptr->PhysicalAddress);
+ else
+ // loopback if it has no address
+ iface->flags |= QNetworkInterface::IsLoopBack;
+
+ // The GetAdaptersAddresses call has an interesting semantic:
+ // It can return a number N of addresses and a number M of prefixes.
+ // But if you have IPv6 addresses, generally N > M.
+ // I cannot find a way to relate the Address to the Prefix, aside from stopping
+ // the iteration at the last Prefix entry and assume that it applies to all addresses
+ // from that point on.
+ PIP_ADAPTER_PREFIX pprefix = 0;
+ if (ptr->Length >= offsetof(IP_ADAPTER_ADDRESSES, FirstPrefix))
+ pprefix = ptr->FirstPrefix;
+ for (PIP_ADAPTER_UNICAST_ADDRESS addr = ptr->FirstUnicastAddress; addr; addr = addr->Next) {
+ QNetworkAddressEntry entry;
+ entry.setIp(addressFromSockaddr(addr->Address.lpSockaddr));
+ if (pprefix) {
+ if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
+ entry.setNetmask(ipv4netmasks[entry.ip()]);
+
+ // broadcast address is set on postProcess()
+ } else { //IPV6
+ entry.setPrefixLength(pprefix->PrefixLength);
+ }
+ pprefix = pprefix->Next ? pprefix->Next : pprefix;
+ }
+ iface->addressEntries << entry;
+ }
+ }
+
+ if (pAdapter != staticBuf)
+ qFree(pAdapter);
+
+ return interfaces;
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListingWin2k()
+{
+ QList<QNetworkInterfacePrivate *> interfaces;
+ IP_ADAPTER_INFO staticBuf[2]; // 2 is arbitrary
+ PIP_ADAPTER_INFO pAdapter = staticBuf;
+ ULONG bufSize = sizeof staticBuf;
+
+ DWORD retval = ptrGetAdaptersInfo(pAdapter, &bufSize);
+ if (retval == ERROR_BUFFER_OVERFLOW) {
+ // need more memory
+ pAdapter = (IP_ADAPTER_INFO *)qMalloc(bufSize);
+ if (!pAdapter)
+ return interfaces;
+ // try again
+ if (ptrGetAdaptersInfo(pAdapter, &bufSize) != ERROR_SUCCESS) {
+ qFree(pAdapter);
+ return interfaces;
+ }
+ } else if (retval != ERROR_SUCCESS) {
+ // error
+ return interfaces;
+ }
+
+ // iterate over the list and add the entries to our listing
+ for (PIP_ADAPTER_INFO ptr = pAdapter; ptr; ptr = ptr->Next) {
+ QNetworkInterfacePrivate *iface = new QNetworkInterfacePrivate;
+ interfaces << iface;
+
+ iface->index = ptr->Index;
+ iface->flags = QNetworkInterface::IsUp | QNetworkInterface::IsRunning;
+ if (ptr->Type == MIB_IF_TYPE_PPP)
+ iface->flags |= QNetworkInterface::IsPointToPoint;
+ else
+ iface->flags |= QNetworkInterface::CanBroadcast;
+ iface->name = QString::fromLocal8Bit(ptr->AdapterName);
+ iface->hardwareAddress = QNetworkInterfacePrivate::makeHwAddress(ptr->AddressLength,
+ ptr->Address);
+
+ for (PIP_ADDR_STRING addr = &ptr->IpAddressList; addr; addr = addr->Next) {
+ QNetworkAddressEntry entry;
+ entry.setIp(QHostAddress(QLatin1String(addr->IpAddress.String)));
+ entry.setNetmask(QHostAddress(QLatin1String(addr->IpMask.String)));
+ // broadcast address is set on postProcess()
+
+ iface->addressEntries << entry;
+ }
+ }
+
+ if (pAdapter != staticBuf)
+ qFree(pAdapter);
+
+ return interfaces;
+}
+
+static QList<QNetworkInterfacePrivate *> interfaceListing()
+{
+ resolveLibs();
+ if (ptrGetAdaptersAddresses != NULL)
+ return interfaceListingWinXP();
+ else if (ptrGetAdaptersInfo != NULL)
+ return interfaceListingWin2k();
+
+ // failed
+ return QList<QNetworkInterfacePrivate *>();
+}
+
+QList<QNetworkInterfacePrivate *> QNetworkInterfaceManager::scan()
+{
+ return interfaceListing();
+}
+
+QString QHostInfo::localDomainName()
+{
+ resolveLibs();
+ if (ptrGetNetworkParams == NULL)
+ return QString(); // couldn't resolve
+
+ FIXED_INFO info, *pinfo;
+ ULONG bufSize = sizeof info;
+ pinfo = &info;
+ if (ptrGetNetworkParams(pinfo, &bufSize) == ERROR_BUFFER_OVERFLOW) {
+ pinfo = (FIXED_INFO *)qMalloc(bufSize);
+ if (!pinfo)
+ return QString();
+ // try again
+ if (ptrGetNetworkParams(pinfo, &bufSize) != ERROR_SUCCESS) {
+ qFree(pinfo);
+ return QString(); // error
+ }
+ }
+
+ QString domainName = QUrl::fromAce(pinfo->DomainName);
+
+ if (pinfo != &info)
+ qFree(pinfo);
+
+ return domainName;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
diff --git a/src/network/kernel/qnetworkinterface_win_p.h b/src/network/kernel/qnetworkinterface_win_p.h
new file mode 100644
index 0000000000..ca15406012
--- /dev/null
+++ b/src/network/kernel/qnetworkinterface_win_p.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKINTERFACE_WIN_P_H
+#define QNETWORKINTERFACE_WIN_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 <winsock2.h>
+#include <qt_windows.h>
+#include <time.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef GAA_FLAG_INCLUDE_ALL_INTERFACES
+# define GAA_FLAG_INCLUDE_ALL_INTERFACES 0x0100
+#endif
+#ifndef MAX_ADAPTER_ADDRESS_LENGTH
+// definitions from iptypes.h
+# define MAX_ADAPTER_DESCRIPTION_LENGTH 128 // arb.
+# define MAX_ADAPTER_NAME_LENGTH 256 // arb.
+# define MAX_ADAPTER_ADDRESS_LENGTH 8 // arb.
+# define DEFAULT_MINIMUM_ENTITIES 32 // arb.
+# define MAX_HOSTNAME_LEN 128 // arb.
+# define MAX_DOMAIN_NAME_LEN 128 // arb.
+# define MAX_SCOPE_ID_LEN 256 // arb.
+
+# define GAA_FLAG_SKIP_UNICAST 0x0001
+# define GAA_FLAG_SKIP_ANYCAST 0x0002
+# define GAA_FLAG_SKIP_MULTICAST 0x0004
+# define GAA_FLAG_SKIP_DNS_SERVER 0x0008
+# define GAA_FLAG_INCLUDE_PREFIX 0x0010
+# define GAA_FLAG_SKIP_FRIENDLY_NAME 0x0020
+
+# define IP_ADAPTER_DDNS_ENABLED 0x01
+# define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX 0x02
+# define IP_ADAPTER_DHCP_ENABLED 0x04
+# define IP_ADAPTER_RECEIVE_ONLY 0x08
+# define IP_ADAPTER_NO_MULTICAST 0x10
+# define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20
+
+# define MIB_IF_TYPE_OTHER 1
+# define MIB_IF_TYPE_ETHERNET 6
+# define MIB_IF_TYPE_TOKENRING 9
+# define MIB_IF_TYPE_FDDI 15
+# define MIB_IF_TYPE_PPP 23
+# define MIB_IF_TYPE_LOOPBACK 24
+# define MIB_IF_TYPE_SLIP 28
+
+#endif
+// copied from qnativesocketengine_win.cpp
+struct qt_in6_addr {
+ u_char qt_s6_addr[16];
+};
+typedef struct {
+ short sin6_family; /* AF_INET6 */
+ u_short sin6_port; /* Transport level port number */
+ u_long sin6_flowinfo; /* IPv6 flow information */
+ struct qt_in6_addr sin6_addr; /* IPv6 address */
+ u_long sin6_scope_id; /* set of interfaces for a scope */
+} qt_sockaddr_in6;
+
+// copied from MSDN online help
+typedef enum {
+ IpPrefixOriginOther = 0,
+ IpPrefixOriginManual,
+ IpPrefixOriginWellKnown,
+ IpPrefixOriginDhcp,
+ IpPrefixOriginRouterAdvertisement
+} IP_PREFIX_ORIGIN;
+
+typedef enum {
+ IpSuffixOriginOther = 0,
+ IpSuffixOriginManual,
+ IpSuffixOriginWellKnown,
+ IpSuffixOriginDhcp,
+ IpSuffixOriginLinkLayerAddress,
+ IpSuffixOriginRandom
+} IP_SUFFIX_ORIGIN;
+
+typedef enum {
+ IpDadStateInvalid = 0,
+ IpDadStateTentative,
+ IpDadStateDuplicate,
+ IpDadStateDeprecated,
+ IpDadStatePreferred,
+} IP_DAD_STATE;
+
+typedef enum {
+ IfOperStatusUp = 1,
+ IfOperStatusDown,
+ IfOperStatusTesting,
+ IfOperStatusUnknown,
+ IfOperStatusDormant,
+ IfOperStatusNotPresent,
+ IfOperStatusLowerLayerDown
+} IF_OPER_STATUS;
+
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
+ union {
+ ULONGLONG Alignment;
+ struct {
+ ULONG Length;
+ DWORD Flags;
+ };
+ };
+ struct _IP_ADAPTER_UNICAST_ADDRESS* Next;
+ SOCKET_ADDRESS Address;
+ IP_PREFIX_ORIGIN PrefixOrigin;
+ IP_SUFFIX_ORIGIN SuffixOrigin;
+ IP_DAD_STATE DadState;
+ ULONG ValidLifetime;
+ ULONG PreferredLifetime;
+ ULONG LeaseLifetime;
+} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
+
+typedef struct _IP_ADAPTER_ANYCAST_ADDRESS
+ IP_ADAPTER_ANYCAST_ADDRESS, *PIP_ADAPTER_ANYCAST_ADDRESS;
+
+typedef struct _IP_ADAPTER_MULTICAST_ADDRESS
+ IP_ADAPTER_MULTICAST_ADDRESS,
+ *PIP_ADAPTER_MULTICAST_ADDRESS;
+
+typedef struct _IP_ADAPTER_DNS_SERVER_ADDRESS
+ IP_ADAPTER_DNS_SERVER_ADDRESS,
+ *PIP_ADAPTER_DNS_SERVER_ADDRESS;
+
+typedef struct _IP_ADAPTER_PREFIX {
+ union {
+ ULONGLONG Alignment;
+ struct {
+ ULONG Length;
+ DWORD Flags;
+ };
+ };
+ struct _IP_ADAPTER_PREFIX* Next;
+ SOCKET_ADDRESS Address;
+ ULONG PrefixLength;
+} IP_ADAPTER_PREFIX,
+ *PIP_ADAPTER_PREFIX;
+
+typedef struct _IP_ADAPTER_ADDRESSES {
+ union {
+ ULONGLONG Alignment;
+ struct {
+ ULONG Length;
+ DWORD IfIndex;
+ };
+ };
+ struct _IP_ADAPTER_ADDRESSES* Next;
+ PCHAR AdapterName;
+ PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
+ PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress;
+ PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress;
+ PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress;
+ PWCHAR DnsSuffix;
+ PWCHAR Description;
+ PWCHAR FriendlyName;
+ BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
+ DWORD PhysicalAddressLength;
+ DWORD Flags;
+ DWORD Mtu;
+ DWORD IfType;
+ IF_OPER_STATUS OperStatus;
+ DWORD Ipv6IfIndex;
+ DWORD ZoneIndices[16];
+ PIP_ADAPTER_PREFIX FirstPrefix;
+} IP_ADAPTER_ADDRESSES,
+ *PIP_ADAPTER_ADDRESSES;
+
+typedef struct {
+ char String[4 * 4];
+} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+
+typedef struct _IP_ADDR_STRING {
+ struct _IP_ADDR_STRING* Next;
+ IP_ADDRESS_STRING IpAddress;
+ IP_MASK_STRING IpMask;
+ DWORD Context;
+} IP_ADDR_STRING,
+ *PIP_ADDR_STRING;
+
+typedef struct _IP_ADAPTER_INFO {
+ struct _IP_ADAPTER_INFO* Next;
+ DWORD ComboIndex;
+ char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
+ char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
+ UINT AddressLength;
+ BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
+ DWORD Index;
+ UINT Type;
+ UINT DhcpEnabled;
+ PIP_ADDR_STRING CurrentIpAddress;
+ IP_ADDR_STRING IpAddressList;
+ IP_ADDR_STRING GatewayList;
+ IP_ADDR_STRING DhcpServer;
+ BOOL HaveWins;
+ IP_ADDR_STRING PrimaryWinsServer;
+ IP_ADDR_STRING SecondaryWinsServer;
+ time_t LeaseObtained;
+ time_t LeaseExpires;
+} IP_ADAPTER_INFO,
+ *PIP_ADAPTER_INFO;
+
+typedef struct {
+ char HostName[MAX_HOSTNAME_LEN + 4];
+ char DomainName[MAX_DOMAIN_NAME_LEN + 4];
+ PIP_ADDR_STRING CurrentDnsServer;
+ IP_ADDR_STRING DnsServerList;
+ UINT NodeType;
+ char ScopeId[MAX_SCOPE_ID_LEN + 4];
+ UINT EnableRouting;
+ UINT EnableProxy;
+ UINT EnableDns;
+} FIXED_INFO, *PFIXED_INFO;
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
new file mode 100644
index 0000000000..68ff95543a
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -0,0 +1,1310 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QNetworkProxy
+
+ \since 4.1
+
+ \brief The QNetworkProxy class provides a network layer proxy.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ QNetworkProxy provides the method for configuring network layer
+ proxy support to the Qt network classes. The currently supported
+ classes are QAbstractSocket, QTcpSocket, QUdpSocket, QTcpServer,
+ QNetworkAccessManager and QFtp. The proxy support is designed to
+ be as transparent as possible. This means that existing
+ network-enabled applications that you have written should
+ automatically support network proxy using the following code.
+
+ \snippet doc/src/snippets/code/src_network_kernel_qnetworkproxy.cpp 0
+
+ An alternative to setting an application wide proxy is to specify
+ the proxy for individual sockets using QAbstractSocket::setProxy()
+ and QTcpServer::setProxy(). In this way, it is possible to disable
+ the use of a proxy for specific sockets using the following code:
+
+ \snippet doc/src/snippets/code/src_network_kernel_qnetworkproxy.cpp 1
+
+ Network proxy is not used if the address used in \l
+ {QAbstractSocket::connectToHost()}{connectToHost()}, \l
+ {QUdpSocket::bind()}{bind()} or \l
+ {QTcpServer::listen()}{listen()} is equivalent to
+ QHostAddress::LocalHost or QHostAddress::LocalHostIPv6.
+
+ Each type of proxy support has certain restrictions associated with it.
+ You should read the \l{ProxyType} documentation carefully before
+ selecting a proxy type to use.
+
+ \note Changes made to currently connected sockets do not take effect.
+ If you need to change a connected socket, you should reconnect it.
+
+ \section1 SOCKS5
+
+ The SOCKS5 support in Qt 4 is based on \l{RFC 1928} and \l{RFC 1929}.
+ The supported authentication methods are no authentication and
+ username/password authentication. Both IPv4 and IPv6 are
+ supported. Domain names are resolved through the SOCKS5 server if
+ the QNetworkProxy::HostNameLookupCapability is enabled, otherwise
+ they are resolved locally and the IP address is sent to the
+ server. There are several things to remember when using SOCKS5
+ with QUdpSocket and QTcpServer:
+
+ With QUdpSocket, a call to \l {QUdpSocket::bind()}{bind()} may fail
+ with a timeout error. If a port number other than 0 is passed to
+ \l {QUdpSocket::bind()}{bind()}, it is not guaranteed that it is the
+ specified port that will be used.
+ Use \l{QUdpSocket::localPort()}{localPort()} and
+ \l{QUdpSocket::localAddress()}{localAddress()} to get the actual
+ address and port number in use. Because proxied UDP goes through
+ two UDP connections, it is more likely that packets will be dropped.
+
+ With QTcpServer a call to \l{QTcpServer::listen()}{listen()} may
+ fail with a timeout error. If a port number other than 0 is passed
+ to \l{QTcpServer::listen()}{listen()}, then it is not guaranteed
+ that it is the specified port that will be used.
+ Use \l{QTcpServer::serverPort()}{serverPort()} and
+ \l{QTcpServer::serverAddress()}{serverAddress()} to get the actual
+ address and port used to listen for connections. SOCKS5 only supports
+ one accepted connection per call to \l{QTcpServer::listen()}{listen()},
+ and each call is likely to result in a different
+ \l{QTcpServer::serverPort()}{serverPort()} being used.
+
+ \sa QAbstractSocket, QTcpServer
+*/
+
+/*!
+ \enum QNetworkProxy::ProxyType
+
+ This enum describes the types of network proxying provided in Qt.
+
+ There are two types of proxies that Qt understands:
+ transparent proxies and caching proxies. The first group consists
+ of proxies that can handle any arbitrary data transfer, while the
+ second can only handle specific requests. The caching proxies only
+ make sense for the specific classes where they can be used.
+
+ \value NoProxy No proxying is used
+ \value DefaultProxy Proxy is determined based on the application proxy set using setApplicationProxy()
+ \value Socks5Proxy \l Socks5 proxying is used
+ \value HttpProxy HTTP transparent proxying is used
+ \value HttpCachingProxy Proxying for HTTP requests only
+ \value FtpCachingProxy Proxying for FTP requests only
+
+ The table below lists different proxy types and their
+ capabilities. Since each proxy type has different capabilities, it
+ is important to understand them before choosing a proxy type.
+
+ \table
+ \header
+ \o Proxy type
+ \o Description
+ \o Default capabilities
+
+ \row
+ \o SOCKS 5
+ \o Generic proxy for any kind of connection. Supports TCP,
+ UDP, binding to a port (incoming connections) and
+ authentication.
+ \o TunnelingCapability, ListeningCapability,
+ UdpTunnelingCapability, HostNameLookupCapability
+
+ \row
+ \o HTTP
+ \o Implemented using the "CONNECT" command, supports only
+ outgoing TCP connections; supports authentication.
+ \o TunnelingCapability, CachingCapability, HostNameLookupCapability
+
+ \row
+ \o Caching-only HTTP
+ \o Implemented using normal HTTP commands, it is useful only
+ in the context of HTTP requests (see QNetworkAccessManager)
+ \o CachingCapability, HostNameLookupCapability
+
+ \row
+ \o Caching FTP
+ \o Implemented using an FTP proxy, it is useful only in the
+ context of FTP requests (see QFtp,
+ QNetworkAccessManager)
+ \o CachingCapability, HostNameLookupCapability
+
+ \endtable
+
+ Also note that you shouldn't set the application default proxy
+ (setApplicationProxy()) to a proxy that doesn't have the
+ TunnelingCapability capability. If you do, QTcpSocket will not
+ know how to open connections.
+
+ \sa setType(), type(), capabilities(), setCapabilities()
+*/
+
+/*!
+ \enum QNetworkProxy::Capability
+ \since 4.5
+
+ These flags indicate the capabilities that a given proxy server
+ supports.
+
+ QNetworkProxy sets different capabilities by default when the
+ object is created (see QNetworkProxy::ProxyType for a list of the
+ defaults). However, it is possible to change the capabitilies
+ after the object has been created with setCapabilities().
+
+ The capabilities that QNetworkProxy supports are:
+
+ \value TunnelingCapability Ability to open transparent, tunneled
+ TCP connections to a remote host. The proxy server relays the
+ transmission verbatim from one side to the other and does no
+ caching.
+
+ \value ListeningCapability Ability to create a listening socket
+ and wait for an incoming TCP connection from a remote host.
+
+ \value UdpTunnelingCapability Ability to relay UDP datagrams via
+ the proxy server to and from a remote host.
+
+ \value CachingCapability Ability to cache the contents of the
+ transfer. This capability is specific to each protocol and proxy
+ type. For example, HTTP proxies can cache the contents of web data
+ transferred with "GET" commands.
+
+ \value HostNameLookupCapability Ability to connect to perform the
+ lookup on a remote host name and connect to it, as opposed to
+ requiring the application to perform the name lookup and request
+ connection to IP addresses only.
+*/
+
+#include "qnetworkproxy.h"
+
+#ifndef QT_NO_NETWORKPROXY
+
+#include "private/qnetworkproxy_p.h"
+#include "private/qsocks5socketengine_p.h"
+#include "private/qhttpsocketengine_p.h"
+#include "qauthenticator.h"
+#include "qhash.h"
+#include "qmutex.h"
+#include "qurl.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSocks5SocketEngineHandler;
+class QHttpSocketEngineHandler;
+
+class QGlobalNetworkProxy
+{
+public:
+ QGlobalNetworkProxy()
+ : mutex(QMutex::Recursive)
+ , applicationLevelProxy(0)
+ , applicationLevelProxyFactory(0)
+ , socks5SocketEngineHandler(0)
+ , httpSocketEngineHandler(0)
+ {
+ }
+
+ ~QGlobalNetworkProxy()
+ {
+ delete applicationLevelProxy;
+ delete applicationLevelProxyFactory;
+ delete socks5SocketEngineHandler;
+ delete httpSocketEngineHandler;
+ }
+
+ void init()
+ {
+ QMutexLocker lock(&mutex);
+#ifndef QT_NO_SOCKS5
+ if (!socks5SocketEngineHandler)
+ socks5SocketEngineHandler = new QSocks5SocketEngineHandler();
+#endif
+#ifndef QT_NO_HTTP
+ if (!httpSocketEngineHandler)
+ httpSocketEngineHandler = new QHttpSocketEngineHandler();
+#endif
+ }
+
+ void setApplicationProxy(const QNetworkProxy &proxy)
+ {
+ QMutexLocker lock(&mutex);
+ if (!applicationLevelProxy)
+ applicationLevelProxy = new QNetworkProxy;
+ *applicationLevelProxy = proxy;
+ delete applicationLevelProxyFactory;
+ applicationLevelProxyFactory = 0;
+ }
+
+ void setApplicationProxyFactory(QNetworkProxyFactory *factory)
+ {
+ QMutexLocker lock(&mutex);
+ if (applicationLevelProxy)
+ *applicationLevelProxy = QNetworkProxy();
+ delete applicationLevelProxyFactory;
+ applicationLevelProxyFactory = factory;
+ }
+
+ QNetworkProxy applicationProxy()
+ {
+ return proxyForQuery(QNetworkProxyQuery()).first();
+ }
+
+ QList<QNetworkProxy> proxyForQuery(const QNetworkProxyQuery &query);
+
+private:
+ QMutex mutex;
+ QNetworkProxy *applicationLevelProxy;
+ QNetworkProxyFactory *applicationLevelProxyFactory;
+ QSocks5SocketEngineHandler *socks5SocketEngineHandler;
+ QHttpSocketEngineHandler *httpSocketEngineHandler;
+};
+
+QList<QNetworkProxy> QGlobalNetworkProxy::proxyForQuery(const QNetworkProxyQuery &query)
+{
+ QMutexLocker locker(&mutex);
+
+ QList<QNetworkProxy> result;
+ if (!applicationLevelProxyFactory) {
+ if (applicationLevelProxy
+ && applicationLevelProxy->type() != QNetworkProxy::DefaultProxy)
+ result << *applicationLevelProxy;
+ else
+ result << QNetworkProxy(QNetworkProxy::NoProxy);
+ return result;
+ }
+
+ // we have a factory
+ result = applicationLevelProxyFactory->queryProxy(query);
+ if (result.isEmpty()) {
+ qWarning("QNetworkProxyFactory: factory %p has returned an empty result set",
+ applicationLevelProxyFactory);
+ result << QNetworkProxy(QNetworkProxy::NoProxy);
+ }
+ return result;
+}
+
+Q_GLOBAL_STATIC(QGlobalNetworkProxy, globalNetworkProxy)
+
+namespace {
+ template<bool> struct StaticAssertTest;
+ template<> struct StaticAssertTest<true> { enum { Value = 1 }; };
+}
+
+static inline void qt_noop_with_arg(int) {}
+#define q_static_assert(expr) qt_noop_with_arg(sizeof(StaticAssertTest< expr >::Value))
+
+static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::ProxyType type)
+{
+ q_static_assert(int(QNetworkProxy::DefaultProxy) == 0);
+ q_static_assert(int(QNetworkProxy::FtpCachingProxy) == 5);
+ static const int defaults[] =
+ {
+ /* [QNetworkProxy::DefaultProxy] = */
+ (int(QNetworkProxy::ListeningCapability) |
+ int(QNetworkProxy::TunnelingCapability) |
+ int(QNetworkProxy::UdpTunnelingCapability)),
+ /* [QNetworkProxy::Socks5Proxy] = */
+ (int(QNetworkProxy::TunnelingCapability) |
+ int(QNetworkProxy::ListeningCapability) |
+ int(QNetworkProxy::UdpTunnelingCapability) |
+ int(QNetworkProxy::HostNameLookupCapability)),
+ // it's weird to talk about the proxy capabilities of a "not proxy"...
+ /* [QNetworkProxy::NoProxy] = */
+ (int(QNetworkProxy::ListeningCapability) |
+ int(QNetworkProxy::TunnelingCapability) |
+ int(QNetworkProxy::UdpTunnelingCapability)),
+ /* [QNetworkProxy::HttpProxy] = */
+ (int(QNetworkProxy::TunnelingCapability) |
+ int(QNetworkProxy::CachingCapability) |
+ int(QNetworkProxy::HostNameLookupCapability)),
+ /* [QNetworkProxy::HttpCachingProxy] = */
+ (int(QNetworkProxy::CachingCapability) |
+ int(QNetworkProxy::HostNameLookupCapability)),
+ /* [QNetworkProxy::FtpCachingProxy] = */
+ (int(QNetworkProxy::CachingCapability) |
+ int(QNetworkProxy::HostNameLookupCapability)),
+ };
+
+ if (int(type) < 0 || int(type) > int(QNetworkProxy::FtpCachingProxy))
+ type = QNetworkProxy::DefaultProxy;
+ return QNetworkProxy::Capabilities(defaults[int(type)]);
+}
+
+class QNetworkProxyPrivate: public QSharedData
+{
+public:
+ QString hostName;
+ QString user;
+ QString password;
+ QNetworkProxy::Capabilities capabilities;
+ quint16 port;
+ QNetworkProxy::ProxyType type;
+ bool capabilitiesSet;
+
+ inline QNetworkProxyPrivate(QNetworkProxy::ProxyType t = QNetworkProxy::DefaultProxy,
+ const QString &h = QString(), quint16 p = 0,
+ const QString &u = QString(), const QString &pw = QString())
+ : hostName(h),
+ user(u),
+ password(pw),
+ capabilities(defaultCapabilitiesForType(t)),
+ port(p),
+ type(t),
+ capabilitiesSet(false)
+ { }
+
+ inline bool operator==(const QNetworkProxyPrivate &other) const
+ {
+ return type == other.type &&
+ port == other.port &&
+ hostName == other.hostName &&
+ user == other.user &&
+ password == other.password &&
+ capabilities == other.capabilities;
+ }
+};
+
+template<> void QSharedDataPointer<QNetworkProxyPrivate>::detach()
+{
+ if (d && d->ref == 1)
+ return;
+ QNetworkProxyPrivate *x = (d ? new QNetworkProxyPrivate(*d)
+ : new QNetworkProxyPrivate);
+ x->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = x;
+}
+
+/*!
+ Constructs a QNetworkProxy with DefaultProxy type; the proxy type is
+ determined by applicationProxy(), which defaults to NoProxy.
+
+ \sa setType(), setApplicationProxy()
+*/
+QNetworkProxy::QNetworkProxy()
+ : d(0)
+{
+ if (QGlobalNetworkProxy *globalProxy = globalNetworkProxy())
+ globalProxy->init();
+}
+
+/*!
+ Constructs a QNetworkProxy with \a type, \a hostName, \a port,
+ \a user and \a password.
+
+ The default capabilities for proxy type \a type are set automatically.
+
+ \sa capabilities()
+*/
+QNetworkProxy::QNetworkProxy(ProxyType type, const QString &hostName, quint16 port,
+ const QString &user, const QString &password)
+ : d(new QNetworkProxyPrivate(type, hostName, port, user, password))
+{
+ if (QGlobalNetworkProxy *globalProxy = globalNetworkProxy())
+ globalProxy->init();
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QNetworkProxy::QNetworkProxy(const QNetworkProxy &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys the QNetworkProxy object.
+*/
+QNetworkProxy::~QNetworkProxy()
+{
+ // QSharedDataPointer takes care of deleting for us
+}
+
+/*!
+ \since 4.4
+
+ Compares the value of this network proxy to \a other and returns true
+ if they are equal (same proxy type, server as well as username and password)
+*/
+bool QNetworkProxy::operator==(const QNetworkProxy &other) const
+{
+ return d == other.d || (d && other.d && *d == *other.d);
+}
+
+/*!
+ \fn bool QNetworkProxy::operator!=(const QNetworkProxy &other) const
+ \since 4.4
+
+ Compares the value of this network proxy to \a other and returns true
+ if they differ.
+\*/
+
+/*!
+ \since 4.2
+
+ Assigns the value of the network proxy \a other to this network proxy.
+*/
+QNetworkProxy &QNetworkProxy::operator=(const QNetworkProxy &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Sets the proxy type for this instance to be \a type.
+
+ Note that changing the type of a proxy does not change
+ the set of capabilities this QNetworkProxy object holds if any
+ capabilities have been set with setCapabilities().
+
+ \sa type(), setCapabilities()
+*/
+void QNetworkProxy::setType(QNetworkProxy::ProxyType type)
+{
+ d->type = type;
+ if (!d->capabilitiesSet)
+ d->capabilities = defaultCapabilitiesForType(type);
+}
+
+/*!
+ Returns the proxy type for this instance.
+
+ \sa setType()
+*/
+QNetworkProxy::ProxyType QNetworkProxy::type() const
+{
+ return d ? d->type : DefaultProxy;
+}
+
+/*!
+ \since 4.5
+
+ Sets the capabilities of this proxy to \a capabilities.
+
+ \sa setType(), capabilities()
+*/
+void QNetworkProxy::setCapabilities(Capabilities capabilities)
+{
+ d->capabilities = capabilities;
+ d->capabilitiesSet = true;
+}
+
+/*!
+ \since 4.5
+
+ Returns the capabilities of this proxy server.
+
+ \sa setCapabilities(), type()
+*/
+QNetworkProxy::Capabilities QNetworkProxy::capabilities() const
+{
+ return d ? d->capabilities : defaultCapabilitiesForType(DefaultProxy);
+}
+
+/*!
+ \since 4.4
+
+ Returns true if this proxy supports the
+ QNetworkProxy::CachingCapability capability.
+
+ In Qt 4.4, the capability was tied to the proxy type, but since Qt
+ 4.5 it is possible to remove the capability of caching from a
+ proxy by calling setCapabilities().
+
+ \sa capabilities(), type(), isTransparentProxy()
+*/
+bool QNetworkProxy::isCachingProxy() const
+{
+ return capabilities() & CachingCapability;
+}
+
+/*!
+ \since 4.4
+
+ Returns true if this proxy supports transparent tunneling of TCP
+ connections. This matches the QNetworkProxy::TunnelingCapability
+ capability.
+
+ In Qt 4.4, the capability was tied to the proxy type, but since Qt
+ 4.5 it is possible to remove the capability of caching from a
+ proxy by calling setCapabilities().
+
+ \sa capabilities(), type(), isCachingProxy()
+*/
+bool QNetworkProxy::isTransparentProxy() const
+{
+ return capabilities() & TunnelingCapability;
+}
+
+/*!
+ Sets the user name for proxy authentication to be \a user.
+
+ \sa user(), setPassword(), password()
+*/
+void QNetworkProxy::setUser(const QString &user)
+{
+ d->user = user;
+}
+
+/*!
+ Returns the user name used for authentication.
+
+ \sa setUser(), setPassword(), password()
+*/
+QString QNetworkProxy::user() const
+{
+ return d ? d->user : QString();
+}
+
+/*!
+ Sets the password for proxy authentication to be \a password.
+
+ \sa user(), setUser(), password()
+*/
+void QNetworkProxy::setPassword(const QString &password)
+{
+ d->password = password;
+}
+
+/*!
+ Returns the password used for authentication.
+
+ \sa user(), setPassword(), setUser()
+*/
+QString QNetworkProxy::password() const
+{
+ return d ? d->password : QString();
+}
+
+/*!
+ Sets the host name of the proxy host to be \a hostName.
+
+ \sa hostName(), setPort(), port()
+*/
+void QNetworkProxy::setHostName(const QString &hostName)
+{
+ d->hostName = hostName;
+}
+
+/*!
+ Returns the host name of the proxy host.
+
+ \sa setHostName(), setPort(), port()
+*/
+QString QNetworkProxy::hostName() const
+{
+ return d ? d->hostName : QString();
+}
+
+/*!
+ Sets the port of the proxy host to be \a port.
+
+ \sa hostName(), setHostName(), port()
+*/
+void QNetworkProxy::setPort(quint16 port)
+{
+ d->port = port;
+}
+
+/*!
+ Returns the port of the proxy host.
+
+ \sa setHostName(), setPort(), hostName()
+*/
+quint16 QNetworkProxy::port() const
+{
+ return d ? d->port : 0;
+}
+
+/*!
+ Sets the application level network proxying to be \a networkProxy.
+
+ If a QAbstractSocket or QTcpSocket has the
+ QNetworkProxy::DefaultProxy type, then the QNetworkProxy set with
+ this function is used. If you want more flexibility in determining
+ which the proxy, use the QNetworkProxyFactory class.
+
+ Setting a default proxy value with this function will override the
+ application proxy factory set with
+ QNetworkProxyFactory::setApplicationProxyFactory.
+
+ \sa QNetworkProxyFactory, applicationProxy(), QAbstractSocket::setProxy(), QTcpServer::setProxy()
+*/
+void QNetworkProxy::setApplicationProxy(const QNetworkProxy &networkProxy)
+{
+ if (globalNetworkProxy()) {
+ // don't accept setting the proxy to DefaultProxy
+ if (networkProxy.type() == DefaultProxy)
+ globalNetworkProxy()->setApplicationProxy(QNetworkProxy::NoProxy);
+ else
+ globalNetworkProxy()->setApplicationProxy(networkProxy);
+ }
+}
+
+/*!
+ Returns the application level network proxying.
+
+ If a QAbstractSocket or QTcpSocket has the
+ QNetworkProxy::DefaultProxy type, then the QNetworkProxy returned
+ by this function is used.
+
+ \sa QNetworkProxyFactory, setApplicationProxy(), QAbstractSocket::proxy(), QTcpServer::proxy()
+*/
+QNetworkProxy QNetworkProxy::applicationProxy()
+{
+ if (globalNetworkProxy())
+ return globalNetworkProxy()->applicationProxy();
+ return QNetworkProxy();
+}
+
+class QNetworkProxyQueryPrivate: public QSharedData
+{
+public:
+ inline QNetworkProxyQueryPrivate()
+ : localPort(-1), type(QNetworkProxyQuery::TcpSocket)
+ { }
+
+ bool operator==(const QNetworkProxyQueryPrivate &other) const
+ {
+ return type == other.type &&
+ localPort == other.localPort &&
+ remote == other.remote;
+ }
+
+ QUrl remote;
+ int localPort;
+ QNetworkProxyQuery::QueryType type;
+};
+
+template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
+{
+ if (d && d->ref == 1)
+ return;
+ QNetworkProxyQueryPrivate *x = (d ? new QNetworkProxyQueryPrivate(*d)
+ : new QNetworkProxyQueryPrivate);
+ x->ref.ref();
+ if (d && !d->ref.deref())
+ delete d;
+ d = x;
+}
+
+/*!
+ \class QNetworkProxyQuery
+ \since 4.5
+ \inmodule QtNetwork
+ \brief The QNetworkProxyQuery class is used to query the proxy
+ settings for a socket
+
+ QNetworkProxyQuery holds the details of a socket being created or
+ request being made. It is used by QNetworkProxy and
+ QNetworkProxyFactory to allow applications to have a more
+ fine-grained control over which proxy servers are used, depending
+ on the details of the query. This allows an application to apply
+ different settings, according to the protocol or destination
+ hostname, for instance.
+
+ QNetworkProxyQuery supports the following criteria for selecting
+ the proxy:
+
+ \list
+ \o the type of query
+ \o the local port number to use
+ \o the destination host name
+ \o the destination port number
+ \o the protocol name, such as "http" or "ftp"
+ \o the URL being requested
+ \endlist
+
+ The destination host name is the host in the connection in the
+ case of outgoing connection sockets. It is the \c hostName
+ parameter passed to QTcpSocket::connectToHost() or the host
+ component of a URL requested with QNetworkRequest.
+
+ The destination port number is the requested port to connect to in
+ the case of outgoing sockets, while the local port number is the
+ port the socket wishes to use locally before attempting the
+ external connection. In most cases, the local port number is used
+ by listening sockets only (QTcpSocket) or by datagram sockets
+ (QUdpSocket).
+
+ The protocol name is an arbitrary string that indicates the type
+ of connection being attempted. For example, it can match the
+ scheme of a URL, like "http", "https" and "ftp". In most cases,
+ the proxy selection will not change depending on the protocol, but
+ this information is provided in case a better choice can be made,
+ like choosing an caching HTTP proxy for HTTP-based connections,
+ but a more powerful SOCKSv5 proxy for all others.
+
+ 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.
+
+ \table
+ \header
+ \o Query type
+ \o Description
+
+ \row
+ \o TcpSocket
+ \o Normal sockets requesting a connection to a remote server,
+ like QTcpSocket. The peer hostname and peer port match the
+ values passed to QTcpSocket::connectToHost(). The local port
+ is usually -1, indicating the socket has no preference in
+ which port should be used. The URL component is not used.
+
+ \row
+ \o UdpSocket
+ \o Datagram-based sockets, which can both send and
+ receive. The local port, remote host or remote port fields
+ can all be used or be left unused, depending on the
+ characteristics of the socket. The URL component is not used.
+
+ \row
+ \o TcpServer
+ \o Passive server sockets that listen on a port and await
+ incoming connections from the network. Normally, only the
+ local port is used, but the remote address could be used in
+ specific circumstances, for example to indicate which remote
+ host a connection is expected from. The URL component is not used.
+
+ \row
+ \o UrlRequest
+ \o A more high-level request, such as those coming from
+ QNetworkAccessManager. These requests will inevitably use an
+ outgoing TCP socket, but the this query type is provided to
+ indicate that more detailed information is present in the URL
+ component. For ease of implementation, the URL's host and
+ port are set as the destination address.
+ \endtable
+
+ It should be noted that any of the criteria may be missing or
+ unknown (an empty QString for the hostname or protocol name, -1
+ for the port numbers). If that happens, the functions executing
+ the query should make their best guess or apply some
+ implementation-defined default values.
+
+ \sa QNetworkProxy, QNetworkProxyFactory, QNetworkAccessManager,
+ QAbstractSocket::setProxy()
+*/
+
+/*!
+ \enum QNetworkProxyQuery::QueryType
+
+ Describes the type of one QNetworkProxyQuery query.
+
+ \value TcpSocket a normal, outgoing TCP socket
+ \value UdpSocket a datagram-based UDP socket, which could send
+ to multiple destinations
+ \value TcpServer a TCP server that listens for incoming
+ connections from the network
+ \value UrlRequest a more complex request which involves loading
+ of a URL
+
+ \sa queryType(), setQueryType()
+*/
+
+/*!
+ Constructs a default QNetworkProxyQuery object. By default, the
+ query type will be QNetworkProxyQuery::TcpSocket.
+*/
+QNetworkProxyQuery::QNetworkProxyQuery()
+{
+}
+
+/*!
+ Constructs a QNetworkProxyQuery with the URL \a requestUrl and
+ sets the query type to \a queryType.
+
+ \sa protocolTag(), peerHostName(), peerPort()
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(const QUrl &requestUrl, QueryType queryType)
+{
+ d->remote = requestUrl;
+ d->type = queryType;
+}
+
+/*!
+ 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.
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(const QString &hostname, int port,
+ const QString &protocolTag,
+ QueryType queryType)
+{
+ d->remote.setScheme(protocolTag);
+ d->remote.setHost(hostname);
+ d->remote.setPort(port);
+ d->type = queryType;
+}
+
+/*!
+ 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.
+
+ 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()
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(quint16 bindPort, const QString &protocolTag,
+ QueryType queryType)
+{
+ d->remote.setScheme(protocolTag);
+ d->localPort = bindPort;
+ d->type = queryType;
+}
+
+/*!
+ Constructs a QNetworkProxyQuery object that is a copy of \a other.
+*/
+QNetworkProxyQuery::QNetworkProxyQuery(const QNetworkProxyQuery &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Destroys this QNetworkProxyQuery object.
+*/
+QNetworkProxyQuery::~QNetworkProxyQuery()
+{
+ // QSharedDataPointer automatically deletes
+}
+
+/*!
+ Copies the contents of \a other.
+*/
+QNetworkProxyQuery &QNetworkProxyQuery::operator=(const QNetworkProxyQuery &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this QNetworkProxyQuery object contains the same
+ data as \a other.
+*/
+bool QNetworkProxyQuery::operator==(const QNetworkProxyQuery &other) const
+{
+ return d == other.d || (d && other.d && *d == *other.d);
+}
+
+/*!
+ \fn bool QNetworkProxyQuery::operator!=(const QNetworkProxyQuery &other) const
+
+ Returns true if this QNetworkProxyQuery object does not contain
+ the same data as \a other.
+*/
+
+/*!
+ Returns the query type.
+*/
+QNetworkProxyQuery::QueryType QNetworkProxyQuery::queryType() const
+{
+ return d ? d->type : TcpSocket;
+}
+
+/*!
+ Sets the query type of this object to be \a type.
+*/
+void QNetworkProxyQuery::setQueryType(QueryType type)
+{
+ d->type = type;
+}
+
+/*!
+ Returns the port number for the outgoing request or -1 if the port
+ number is not known.
+
+ If the query type is QNetworkProxyQuery::UrlRequest, this function
+ returns the port number of the URL being requested. In general,
+ frameworks will fill in the port number from their default values.
+
+ \sa peerHostName(), localPort(), setPeerPort()
+*/
+int QNetworkProxyQuery::peerPort() const
+{
+ return d ? d->remote.port() : -1;
+}
+
+/*!
+ Sets the requested port number for the outgoing connection to be
+ \a port. Valid values are 1 to 65535, or -1 to indicate that the
+ remote port number is unknown.
+
+ The peer port number can also be used to indicate the expected
+ port number of an incoming connection in the case of
+ QNetworkProxyQuery::UdpSocket or QNetworkProxyQuery::TcpServer
+ query types.
+
+ \sa peerPort(), setPeerHostName(), setLocalPort()
+*/
+void QNetworkProxyQuery::setPeerPort(int port)
+{
+ d->remote.setPort(port);
+}
+
+/*!
+ Returns the host name or IP address being of the outgoing
+ connection being requested, or an empty string if the remote
+ hostname is not known.
+
+ If the query type is QNetworkProxyQuery::UrlRequest, this function
+ returns the host component of the URL being requested.
+
+ \sa peerPort(), localPort(), setPeerHostName()
+*/
+QString QNetworkProxyQuery::peerHostName() const
+{
+ return d ? d->remote.host() : QString();
+}
+
+/*!
+ Sets the hostname of the outgoing connection being requested to \a
+ hostname. An empty hostname can be used to indicate that the
+ remote host is unknown.
+
+ The peer host name can also be used to indicate the expected
+ source address of an incoming connection in the case of
+ QNetworkProxyQuery::UdpSocket or QNetworkProxyQuery::TcpServer
+ query types.
+
+ \sa peerHostName(), setPeerPort(), setLocalPort()
+*/
+void QNetworkProxyQuery::setPeerHostName(const QString &hostname)
+{
+ d->remote.setHost(hostname);
+}
+
+/*!
+ Returns the port number of the socket that will accept incoming
+ packets from remote servers or -1 if the port is not known.
+
+ \sa peerPort(), peerHostName(), setLocalPort()
+*/
+int QNetworkProxyQuery::localPort() const
+{
+ return d ? d->localPort : -1;
+}
+
+/*!
+ Sets the port number that the socket wishes to use locally to
+ accept incoming packets from remote servers to \a port. The local
+ port is most often used with the QNetworkProxyQuery::TcpServer
+ and QNetworkProxyQuery::UdpSocket query types.
+
+ Valid values are 0 to 65535 (with 0 indicating that any port
+ number will be acceptable) or -1, which means the local port
+ number is unknown or not applicable.
+
+ In some circumstances, for special protocols, it's the local port
+ number can also be used with a query of type
+ QNetworkProxyQuery::TcpSocket. When that happens, the socket is
+ indicating it wishes to use the port number \a port when
+ connecting to a remote host.
+
+ \sa localPort(), setPeerPort(), setPeerHostName()
+*/
+void QNetworkProxyQuery::setLocalPort(int port)
+{
+ d->localPort = port;
+}
+
+/*!
+ Returns the protocol tag for this QNetworkProxyQuery object, or an
+ empty QString in case the protocol tag is unknown.
+
+ In the case of queries of type QNetworkProxyQuery::UrlRequest,
+ this function returns the value of the scheme component of the
+ URL.
+
+ \sa setProtocolTag(), url()
+*/
+QString QNetworkProxyQuery::protocolTag() const
+{
+ return d ? d->remote.scheme() : QString();
+}
+
+/*!
+ Sets the protocol tag for this QNetworkProxyQuery object to be \a
+ protocolTag.
+
+ The protocol tag is an arbitrary string that indicates which
+ protocol is being talked over the socket, such as "http", "xmpp",
+ "telnet", etc. The protocol tag is used by the backend to
+ return a request that is more specific to the protocol in
+ question: for example, a HTTP connection could be use a caching
+ HTTP proxy server, while all other connections use a more powerful
+ SOCKSv5 proxy server.
+
+ \sa protocolTag()
+*/
+void QNetworkProxyQuery::setProtocolTag(const QString &protocolTag)
+{
+ d->remote.setScheme(protocolTag);
+}
+
+/*!
+ Returns the URL component of this QNetworkProxyQuery object in
+ case of a query of type QNetworkProxyQuery::UrlRequest.
+
+ \sa setUrl()
+*/
+QUrl QNetworkProxyQuery::url() const
+{
+ return d ? d->remote : QUrl();
+}
+
+/*!
+ Sets the URL component of this QNetworkProxyQuery object to be \a
+ url. Setting the URL will also set the protocol tag, the remote
+ host name and port number. This is done so as to facilitate the
+ implementation of the code that determines the proxy server to be
+ used.
+
+ \sa url(), peerHostName(), peerPort()
+*/
+void QNetworkProxyQuery::setUrl(const QUrl &url)
+{
+ d->remote = url;
+}
+
+/*!
+ \class QNetworkProxyFactory
+ \brief The QNetworkProxyFactory class provides fine-grained proxy selection.
+ \since 4.5
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ QNetworkProxyFactory is an extension to QNetworkProxy, allowing
+ applications to have a more fine-grained control over which proxy
+ servers are used, depending on the socket requesting the
+ proxy. This allows an application to apply different settings,
+ according to the protocol or destination hostname, for instance.
+
+ QNetworkProxyFactory can be set globally for an application, in
+ which case it will override any global proxies set with
+ QNetworkProxy::setApplicationProxy(). If set globally, any sockets
+ created with Qt will query the factory to determine the proxy to
+ be used.
+
+ A factory can also be set in certain frameworks that support
+ multiple connections, such as QNetworkAccessManager. When set on
+ such object, the factory will be queried for sockets created by
+ that framework only.
+
+ \section1 System Proxies
+
+ You can configure a factory to use the system proxy's settings.
+ Call the setUseSystemConfiguration() function with true to enable
+ this behavior, or false to disable it.
+
+ Similarly, you can use a factory to make queries directly to the
+ system proxy by calling its systemProxyForQuery() function.
+
+ \warning Depending on the configuration of the user's system, the
+ use of system proxy features on certain platforms may be subject
+ to limitations. The systemProxyForQuery() documentation contains a
+ list of these limitations for those platforms that are affected.
+*/
+
+/*!
+ Creates a QNetworkProxyFactory object.
+
+ Since QNetworkProxyFactory is an abstract class, you cannot create
+ objects of type QNetworkProxyFactory directly.
+*/
+QNetworkProxyFactory::QNetworkProxyFactory()
+{
+}
+
+/*!
+ Destroys the QNetworkProxyFactory object.
+*/
+QNetworkProxyFactory::~QNetworkProxyFactory()
+{
+}
+
+
+/*!
+ \since 4.6
+
+ Enables the use of the platform-specific proxy settings, and only those.
+ See systemProxyForQuery() for more information.
+
+ Internally, this method (when called with \a enable set to true)
+ sets an application-wide proxy factory. For this reason, this method
+ is mutually exclusive with setApplicationProxyFactory(): calling
+ setApplicationProxyFactory() overrides the use of the system-wide proxy,
+ and calling setUseSystemConfiguration() overrides any
+ application proxy or proxy factory that was previously set.
+
+ \note See the systemProxyForQuery() documentation for a list of
+ limitations related to the use of system proxies.
+*/
+void QNetworkProxyFactory::setUseSystemConfiguration(bool enable)
+{
+ if (enable) {
+ setApplicationProxyFactory(new QSystemConfigurationProxyFactory);
+ } else {
+ setApplicationProxyFactory(0);
+ }
+}
+
+/*!
+ Sets the application-wide proxy factory to be \a factory. This
+ function will take ownership of that object and will delete it
+ when necessary.
+
+ The application-wide proxy is used as a last-resort when all other
+ proxy selection requests returned QNetworkProxy::DefaultProxy. For
+ example, QTcpSocket objects can have a proxy set with
+ QTcpSocket::setProxy, but if none is set, the proxy factory class
+ set with this function will be queried.
+
+ If you set a proxy factory with this function, any application
+ level proxies set with QNetworkProxy::setApplicationProxy will be
+ overridden.
+
+ \sa QNetworkProxy::setApplicationProxy(),
+ QAbstractSocket::proxy(), QAbstractSocket::setProxy()
+*/
+void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *factory)
+{
+ if (globalNetworkProxy())
+ globalNetworkProxy()->setApplicationProxyFactory(factory);
+}
+
+/*!
+ \fn QList<QNetworkProxy> QNetworkProxyFactory::queryProxy(const QNetworkProxyQuery &query)
+
+ This function examines takes the query request, \a query,
+ examines the details of the type of socket or request and returns
+ a list of QNetworkProxy objects that indicate the proxy servers to
+ be used, in order of preference.
+
+ When reimplementing this class, take care to return at least one
+ element.
+
+ If you cannot determine a better proxy alternative, use
+ QNetworkProxy::DefaultProxy, which tells the code querying for a
+ proxy to use a higher alternative. For example, if this factory is
+ set to a QNetworkAccessManager object, DefaultProxy will tell it
+ to query the application-level proxy settings.
+
+ If this factory is set as the application proxy factory,
+ DefaultProxy and NoProxy will have the same meaning.
+*/
+
+/*!
+ \fn QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
+
+ This function examines takes the query request, \a query,
+ examines the details of the type of socket or request and returns
+ a list of QNetworkProxy objects that indicate the proxy servers to
+ be used, in order of preference.
+
+ This function can be used to determine the platform-specific proxy
+ settings. This function will use the libraries provided by the
+ operating system to determine the proxy for a given connection, if
+ such libraries exist. If they don't, this function will just return a
+ QNetworkProxy of type QNetworkProxy::NoProxy.
+
+ On Windows, this function will use the WinHTTP DLL functions. Despite
+ its name, Microsoft suggests using it for all applications that
+ require network connections, not just HTTP. This will respect the
+ proxy settings set on the registry with the proxycfg.exe tool. If
+ those settings are not found, this function will attempt to obtain
+ Internet Explorer's settings and use them.
+
+ On MacOS X, this function will obtain the proxy settings using the
+ SystemConfiguration framework from Apple. It will apply the FTP,
+ HTTP and HTTPS proxy configurations for queries that contain the
+ protocol tag "ftp", "http" and "https", respectively. If the SOCKS
+ proxy is enabled in that configuration, this function will use the
+ SOCKS server for all queries. If SOCKS isn't enabled, it will use
+ the HTTPS proxy for all TcpSocket and UrlRequest queries.
+
+ On other systems, there is no standardised method of obtaining the
+ system proxy configuration. This function may be improved in
+ future versions to support those systems.
+
+ \section1 Limitations
+
+ These are the limitations for the current version of this
+ function. Future versions of Qt may lift some of the limitations
+ listed here.
+
+ \list
+ \o On MacOS X, this function will ignore the Proxy Auto Configuration
+ settings, since it cannot execute the associated ECMAScript code.
+
+ \o On Windows platforms, this function may take several seconds to
+ execute depending on the configuration of the user's system.
+ \endlist
+*/
+
+/*!
+ This function examines takes the query request, \a query,
+ examines the details of the type of socket or request and returns
+ a list of QNetworkProxy objects that indicate the proxy servers to
+ be used, in order of preference.
+*/
+QList<QNetworkProxy> QNetworkProxyFactory::proxyForQuery(const QNetworkProxyQuery &query)
+{
+ if (!globalNetworkProxy())
+ return QList<QNetworkProxy>() << QNetworkProxy(QNetworkProxy::NoProxy);
+ return globalNetworkProxy()->proxyForQuery(query);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKPROXY
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
new file mode 100644
index 0000000000..26562d533f
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy.h
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKPROXY_H
+#define QNETWORKPROXY_H
+
+#include <QtNetwork/qhostaddress.h>
+#include <QtCore/qshareddata.h>
+
+#ifndef QT_NO_NETWORKPROXY
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QUrl;
+
+class QNetworkProxyQueryPrivate;
+class Q_NETWORK_EXPORT QNetworkProxyQuery
+{
+public:
+ enum QueryType {
+ TcpSocket,
+ UdpSocket,
+ TcpServer = 100,
+ UrlRequest
+ };
+
+ QNetworkProxyQuery();
+ QNetworkProxyQuery(const QUrl &requestUrl, QueryType queryType = UrlRequest);
+ QNetworkProxyQuery(const QString &hostname, int port, const QString &protocolTag = QString(),
+ QueryType queryType = TcpSocket);
+ QNetworkProxyQuery(quint16 bindPort, const QString &protocolTag = QString(),
+ QueryType queryType = TcpServer);
+ QNetworkProxyQuery(const QNetworkProxyQuery &other);
+ ~QNetworkProxyQuery();
+ QNetworkProxyQuery &operator=(const QNetworkProxyQuery &other);
+ bool operator==(const QNetworkProxyQuery &other) const;
+ inline bool operator!=(const QNetworkProxyQuery &other) const
+ { return !(*this == other); }
+
+ QueryType queryType() const;
+ void setQueryType(QueryType type);
+
+ int peerPort() const;
+ void setPeerPort(int port);
+
+ QString peerHostName() const;
+ void setPeerHostName(const QString &hostname);
+
+ int localPort() const;
+ void setLocalPort(int port);
+
+ QString protocolTag() const;
+ void setProtocolTag(const QString &protocolTag);
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+private:
+ QSharedDataPointer<QNetworkProxyQueryPrivate> d;
+};
+Q_DECLARE_TYPEINFO(QNetworkProxyQuery, Q_MOVABLE_TYPE);
+
+class QNetworkProxyPrivate;
+
+class Q_NETWORK_EXPORT QNetworkProxy
+{
+public:
+ enum ProxyType {
+ DefaultProxy,
+ Socks5Proxy,
+ NoProxy,
+ HttpProxy,
+ HttpCachingProxy,
+ FtpCachingProxy
+ };
+
+ enum Capability {
+ TunnelingCapability = 0x0001,
+ ListeningCapability = 0x0002,
+ UdpTunnelingCapability = 0x0004,
+ CachingCapability = 0x0008,
+ HostNameLookupCapability = 0x0010
+ };
+ Q_DECLARE_FLAGS(Capabilities, Capability)
+
+ QNetworkProxy();
+ QNetworkProxy(ProxyType type, const QString &hostName = QString(), quint16 port = 0,
+ const QString &user = QString(), const QString &password = QString());
+ QNetworkProxy(const QNetworkProxy &other);
+ QNetworkProxy &operator=(const QNetworkProxy &other);
+ ~QNetworkProxy();
+ bool operator==(const QNetworkProxy &other) const;
+ inline bool operator!=(const QNetworkProxy &other) const
+ { return !(*this == other); }
+
+ void setType(QNetworkProxy::ProxyType type);
+ QNetworkProxy::ProxyType type() const;
+
+ void setCapabilities(Capabilities capab);
+ Capabilities capabilities() const;
+ bool isCachingProxy() const;
+ bool isTransparentProxy() const;
+
+ void setUser(const QString &userName);
+ QString user() const;
+
+ void setPassword(const QString &password);
+ QString password() const;
+
+ void setHostName(const QString &hostName);
+ QString hostName() const;
+
+ void setPort(quint16 port);
+ quint16 port() const;
+
+ static void setApplicationProxy(const QNetworkProxy &proxy);
+ static QNetworkProxy applicationProxy();
+
+private:
+ QSharedDataPointer<QNetworkProxyPrivate> d;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QNetworkProxy::Capabilities)
+
+class Q_NETWORK_EXPORT QNetworkProxyFactory
+{
+public:
+ QNetworkProxyFactory();
+ virtual ~QNetworkProxyFactory();
+
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()) = 0;
+
+ static void setUseSystemConfiguration(bool enable);
+ static void setApplicationProxyFactory(QNetworkProxyFactory *factory);
+ static QList<QNetworkProxy> proxyForQuery(const QNetworkProxyQuery &query);
+ static QList<QNetworkProxy> systemProxyForQuery(const QNetworkProxyQuery &query = QNetworkProxyQuery());
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_NETWORKPROXY
+
+#endif // QHOSTINFO_H
diff --git a/src/network/kernel/qnetworkproxy_generic.cpp b/src/network/kernel/qnetworkproxy_generic.cpp
new file mode 100644
index 0000000000..1591d855c6
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy_generic.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkproxy.h"
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*
+ * No system proxy. Just return a list with NoProxy.
+ */
+
+QT_BEGIN_NAMESPACE
+
+QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &)
+{
+ return QList<QNetworkProxy>() << QNetworkProxy::NoProxy;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
new file mode 100644
index 0000000000..6fe35ae9f0
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkproxy.h"
+
+#ifndef QT_NO_NETWORKPROXY
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+#include <QtCore/QRegExp>
+#include <QtCore/QStringList>
+#include <QtCore/qendian.h>
+#include <QtCore/qstringlist.h>
+#include "private/qcore_mac_p.h"
+
+/*
+ * MacOS X has a proxy configuration module in System Preferences (on
+ * MacOS X 10.5, it's in Network, Advanced), where one can set the
+ * proxy settings for:
+ *
+ * \list
+ * \o FTP proxy
+ * \o Web Proxy (HTTP)
+ * \o Secure Web Proxy (HTTPS)
+ * \o Streaming Proxy (RTSP)
+ * \o SOCKS Proxy
+ * \o Gopher Proxy
+ * \o URL for Automatic Proxy Configuration (PAC scripts)
+ * \o Bypass list (by default: *.local, 169.254/16)
+ * \endlist
+ *
+ * The matching configuration can be obtained by calling SCDynamicStoreCopyProxies
+ * (from <SystemConfiguration/SCDynamicStoreCopySpecific.h>). See
+ * Apple's documentation:
+ *
+ * http://developer.apple.com/DOCUMENTATION/Networking/Reference/SysConfig/SCDynamicStoreCopySpecific/CompositePage.html#//apple_ref/c/func/SCDynamicStoreCopyProxies
+ *
+ */
+
+QT_BEGIN_NAMESPACE
+
+static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
+{
+ if (host.isEmpty())
+ return true;
+
+ bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':'));
+ CFNumberRef excludeSimples;
+ if (isSimple &&
+ (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExcludeSimpleHostnames))) {
+ int enabled;
+ if (CFNumberGetValue(excludeSimples, kCFNumberIntType, &enabled) && enabled)
+ return true;
+ }
+
+ QHostAddress ipAddress;
+ bool isIpAddress = ipAddress.setAddress(host);
+
+ // not a simple host name
+ // does it match the list of exclusions?
+ CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList);
+ if (!exclusionList)
+ return false;
+
+ CFIndex size = CFArrayGetCount(exclusionList);
+ for (CFIndex i = 0; i < size; ++i) {
+ CFStringRef cfentry = (CFStringRef)CFArrayGetValueAtIndex(exclusionList, i);
+ QString entry = QCFString::toQString(cfentry);
+
+ if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) {
+ return true; // excluded
+ } else {
+ // do wildcard matching
+ QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (rx.exactMatch(host))
+ return true;
+ }
+ }
+
+ // host was not excluded
+ return false;
+}
+
+static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict, QNetworkProxy::ProxyType type,
+ CFStringRef enableKey, CFStringRef hostKey,
+ CFStringRef portKey)
+{
+ CFNumberRef protoEnabled;
+ CFNumberRef protoPort;
+ CFStringRef protoHost;
+ if (enableKey
+ && (protoEnabled = (CFNumberRef)CFDictionaryGetValue(dict, enableKey))
+ && (protoHost = (CFStringRef)CFDictionaryGetValue(dict, hostKey))
+ && (protoPort = (CFNumberRef)CFDictionaryGetValue(dict, portKey))) {
+ int enabled;
+ if (CFNumberGetValue(protoEnabled, kCFNumberIntType, &enabled) && enabled) {
+ QString host = QCFString::toQString(protoHost);
+
+ int port;
+ CFNumberGetValue(protoPort, kCFNumberIntType, &port);
+
+ return QNetworkProxy(type, host, port);
+ }
+ }
+
+ // proxy not enabled
+ return QNetworkProxy();
+}
+
+QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
+{
+ QList<QNetworkProxy> result;
+
+ // obtain a dictionary to the proxy settings:
+ CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+ if (!dict) {
+ qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL");
+ return result; // failed
+ }
+
+ if (isHostExcluded(dict, query.peerHostName())) {
+ CFRelease(dict);
+ return result; // no proxy for this host
+ }
+
+ // is there a PAC enabled? If so, use it first.
+ CFNumberRef pacEnabled;
+ if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) {
+ int enabled;
+ if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
+ // PAC is enabled
+ CFStringRef pacUrl =
+ (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
+ QString url = QCFString::toQString(pacUrl);
+
+ // ### TODO: Use PAC somehow
+ }
+ }
+
+ // no PAC, decide which proxy we're looking for based on the query
+ bool isHttps = false;
+ QString protocol = query.protocolTag().toLower();
+
+ // try the protocol-specific proxy
+ QNetworkProxy protocolSpecificProxy;
+ if (protocol == QLatin1String("ftp")) {
+ protocolSpecificProxy =
+ proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
+ kSCPropNetProxiesFTPEnable,
+ kSCPropNetProxiesFTPProxy,
+ kSCPropNetProxiesFTPPort);
+ } else if (protocol == QLatin1String("http")) {
+ protocolSpecificProxy =
+ proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
+ kSCPropNetProxiesHTTPEnable,
+ kSCPropNetProxiesHTTPProxy,
+ kSCPropNetProxiesHTTPPort);
+ } else if (protocol == QLatin1String("https")) {
+ isHttps = true;
+ protocolSpecificProxy =
+ proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
+ kSCPropNetProxiesHTTPSEnable,
+ kSCPropNetProxiesHTTPSProxy,
+ kSCPropNetProxiesHTTPSPort);
+ }
+ if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy)
+ result << protocolSpecificProxy;
+
+ // let's add SOCKSv5 if present too
+ QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy,
+ kSCPropNetProxiesSOCKSEnable,
+ kSCPropNetProxiesSOCKSProxy,
+ kSCPropNetProxiesSOCKSPort);
+ if (socks5.type() != QNetworkProxy::DefaultProxy)
+ result << socks5;
+
+ // let's add the HTTPS proxy if present (and if we haven't added
+ // yet)
+ if (!isHttps) {
+ QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
+ kSCPropNetProxiesHTTPSEnable,
+ kSCPropNetProxiesHTTPSProxy,
+ kSCPropNetProxiesHTTPSPort);
+ if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy)
+ result << https;
+ }
+
+ CFRelease(dict);
+ return result;
+}
+
+QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
+{
+ QList<QNetworkProxy> result = macQueryInternal(query);
+ if (result.isEmpty())
+ result << QNetworkProxy::NoProxy;
+
+ return result;
+}
+
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkproxy_p.h b/src/network/kernel/qnetworkproxy_p.h
new file mode 100644
index 0000000000..21d2cb0ed5
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 David Faure <dfaure@kdab.net>
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKPROXY_P_H
+#define QNETWORKPROXY_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.
+//
+
+#ifndef QT_NO_NETWORKPROXY
+
+QT_BEGIN_NAMESPACE
+
+class QSystemConfigurationProxyFactory : public QNetworkProxyFactory
+{
+public:
+ QSystemConfigurationProxyFactory() : QNetworkProxyFactory() {}
+
+ virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery& query)
+ {
+ QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery(query);
+
+ // Make sure NoProxy is in the list, so that QTcpServer can work:
+ // it searches for the first proxy that can has the ListeningCapability capability
+ // if none have (as is the case with HTTP proxies), it fails to bind.
+ // NoProxy allows it to fallback to the 'no proxy' case and bind.
+ proxies.append(QNetworkProxy::NoProxy);
+
+ return proxies;
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKINTERFACE
+
+#endif
+
diff --git a/src/network/kernel/qnetworkproxy_symbian.cpp b/src/network/kernel/qnetworkproxy_symbian.cpp
new file mode 100644
index 0000000000..79dfb27396
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy_symbian.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** 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 FOO module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/**
+ * Some notes about the code:
+ *
+ * ** It is assumed that the system proxies are for url based requests
+ * ie. HTTP/HTTPS based.
+ * ** It is assumed that proxies don't use authentication.
+ * ** It is assumed that there is no exceptions to proxy use (Symbian side
+ * does have the field for it but it is not user modifiable by default).
+ * ** There is no checking for protocol name.
+ */
+
+#include <QtNetwork/qnetworkproxy.h>
+
+#ifndef QT_NO_NETWORKPROXY
+
+#include <metadatabase.h> // CMDBSession
+#include <commsdattypeinfov1_1.h> // CCDIAPRecord, CCDProxiesRecord
+#include <commsdattypesv1_1.h> // KCDTIdIAPRecord, KCDTIdProxiesRecord
+#include <QtNetwork/QNetworkConfigurationManager>
+#include <QFlags>
+
+using namespace CommsDat;
+
+QT_BEGIN_NAMESPACE
+
+class SymbianIapId
+{
+public:
+ enum State{
+ NotValid,
+ Valid
+ };
+ Q_DECLARE_FLAGS(States, State)
+ SymbianIapId() {}
+ ~SymbianIapId() {}
+ void setIapId(TUint32 iapId) { iapState |= Valid; id = iapId; }
+ bool isValid() { return iapState == Valid; }
+ TUint32 iapId() { return id; }
+private:
+ QFlags<States> iapState;
+ TUint32 id;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(SymbianIapId::States)
+
+class SymbianProxyQuery
+{
+public:
+ static QNetworkConfiguration findCurrentConfiguration(QNetworkConfigurationManager& configurationManager);
+ static SymbianIapId getIapId(QNetworkConfigurationManager& configurationManager);
+ 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::findCurrentConfiguration(QNetworkConfigurationManager& configurationManager)
+{
+ QList<QNetworkConfiguration> activeConfigurations = configurationManager.allConfigurations(
+ QNetworkConfiguration::Active);
+ QNetworkConfiguration currentConfig;
+ if (activeConfigurations.count() > 0) {
+ currentConfig = 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;
+ 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;
+ case QNetworkConfiguration::UserChoice:
+ // User choice is not a valid configuration for proxy discovery
+ break;
+ }
+ }
+ }
+ return currentConfig;
+}
+
+SymbianIapId SymbianProxyQuery::getIapId(QNetworkConfigurationManager& configurationManager)
+{
+ SymbianIapId iapId;
+
+ QNetworkConfiguration currentConfig = findCurrentConfiguration(configurationManager);
+ if (currentConfig.isValid()) {
+ // 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
+ // implementation specific identifier.
+ const int generalPartLength = 2;
+ const int identifierNumberLength = currentConfig.identifier().length() - generalPartLength;
+ QString idString(currentConfig.identifier().right(identifierNumberLength));
+ bool success;
+ uint id = idString.toUInt(&success);
+ if (success)
+ iapId.setIapId(id);
+ else
+ qWarning() << "Failed to convert identifier to access point identifier: "
+ << currentConfig.identifier();
+ }
+
+ return iapId;
+}
+
+CCDIAPRecord *SymbianProxyQuery::getIapRecordLC(TUint32 aIAPId, CMDBSession &aDb)
+{
+ CCDIAPRecord *iap = static_cast<CCDIAPRecord*> (CCDRecordBase::RecordFactoryL(KCDTIdIAPRecord));
+ CleanupStack::PushL(iap);
+ iap->SetRecordId(aIAPId);
+ iap->LoadL(aDb);
+ return iap;
+}
+
+CMDBRecordSet<CCDProxiesRecord> *SymbianProxyQuery::prepareQueryLC(TUint32 serviceId, TDesC& serviceType)
+{
+ // Create a recordset of type CCDProxiesRecord
+ // for priming search.
+ // This will ultimately contain record(s)
+ // matching the priming record attributes
+ CMDBRecordSet<CCDProxiesRecord> *proxyRecords = new (ELeave) CMDBRecordSet<CCDProxiesRecord> (
+ KCDTIdProxiesRecord);
+ CleanupStack::PushL(proxyRecords);
+
+ CCDProxiesRecord *primingProxyRecord =
+ static_cast<CCDProxiesRecord *> (CCDRecordBase::RecordFactoryL(KCDTIdProxiesRecord));
+ CleanupStack::PushL(primingProxyRecord);
+
+ primingProxyRecord->iServiceType.SetMaxLengthL(serviceType.Length());
+ primingProxyRecord->iServiceType = serviceType;
+ primingProxyRecord->iService = serviceId;
+ primingProxyRecord->iUseProxyServer = ETrue;
+
+ proxyRecords->iRecords.AppendL(primingProxyRecord);
+ // Ownership of primingProxyRecord is transferred to
+ // proxyRecords, just remove it from the CleanupStack
+ CleanupStack::Pop(primingProxyRecord);
+ return proxyRecords;
+}
+
+QList<QNetworkProxy> SymbianProxyQuery::proxyQueryL(TUint32 aIAPId, const QNetworkProxyQuery &query)
+{
+ QList<QNetworkProxy> foundProxies;
+ if (query.queryType() != QNetworkProxyQuery::UrlRequest) {
+ return foundProxies;
+ }
+
+ CMDBSession *iDb = CMDBSession::NewLC(KCDVersion1_1);
+ CCDIAPRecord *iap = getIapRecordLC(aIAPId, *iDb);
+
+ // Read service table id and service type
+ // from the IAP record found
+ TUint32 serviceId = iap->iService;
+ RBuf serviceType;
+ serviceType.CreateL(iap->iServiceType);
+ CleanupStack::PopAndDestroy(iap);
+ CleanupClosePushL(serviceType);
+
+ CMDBRecordSet<CCDProxiesRecord> *proxyRecords = prepareQueryLC(serviceId, serviceType);
+
+ // Now to find a proxy table matching our criteria
+ if (proxyRecords->FindL(*iDb)) {
+ TInt count = proxyRecords->iRecords.Count();
+ for(TInt index = 0; index < count; index++) {
+ CCDProxiesRecord *proxyRecord = static_cast<CCDProxiesRecord *> (proxyRecords->iRecords[index]);
+ RBuf serverName;
+ serverName.CreateL(proxyRecord->iServerName);
+ CleanupClosePushL(serverName);
+ if (serverName.Length() == 0)
+ User::Leave(KErrNotFound);
+ QString serverNameQt((const QChar*)serverName.Ptr(), serverName.Length());
+ CleanupStack::Pop(); // serverName
+ TUint32 port = proxyRecord->iPortNumber;
+
+ QNetworkProxy proxy(QNetworkProxy::HttpProxy, serverNameQt, port);
+ foundProxies.append(proxy);
+ }
+ }
+
+ CleanupStack::PopAndDestroy(proxyRecords);
+ CleanupStack::Pop(); // serviceType
+ CleanupStack::PopAndDestroy(iDb);
+
+ return foundProxies;
+}
+
+QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
+{
+ QList<QNetworkProxy> proxies;
+ SymbianIapId iapId;
+ TInt error;
+ QNetworkConfigurationManager manager;
+ iapId = SymbianProxyQuery::getIapId(manager);
+ if (iapId.isValid()) {
+ TRAP(error, proxies = SymbianProxyQuery::proxyQueryL(iapId.iapId(), query))
+ if (error != KErrNone) {
+ qWarning() << "Error while retrieving proxies: '" << error << '"';
+ proxies.clear();
+ }
+ }
+ proxies << QNetworkProxy::NoProxy;
+
+ return proxies;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
new file mode 100644
index 0000000000..3e374037db
--- /dev/null
+++ b/src/network/kernel/qnetworkproxy_win.cpp
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkproxy.h"
+
+#ifndef QT_NO_NETWORKPROXY
+
+#include <qmutex.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+#include <qurl.h>
+
+#include <string.h>
+#include <qt_windows.h>
+#include <wininet.h>
+#include <private/qsystemlibrary_p.h>
+
+/*
+ * Information on the WinHTTP DLL:
+ * http://msdn.microsoft.com/en-us/library/aa384122(VS.85).aspx example for WPAD
+ *
+ * http://msdn.microsoft.com/en-us/library/aa384097(VS.85).aspx WinHttpGetProxyForUrl
+ * http://msdn.microsoft.com/en-us/library/aa384096(VS.85).aspx WinHttpGetIEProxyConfigForCurrentUs
+ * http://msdn.microsoft.com/en-us/library/aa384095(VS.85).aspx WinHttpGetDefaultProxyConfiguration
+ */
+
+// We don't want to include winhttp.h because that's not
+// present in some Windows SDKs (I don't know why)
+// So, instead, copy the definitions here
+
+typedef struct {
+ DWORD dwFlags;
+ DWORD dwAutoDetectFlags;
+ LPCWSTR lpszAutoConfigUrl;
+ LPVOID lpvReserved;
+ DWORD dwReserved;
+ BOOL fAutoLogonIfChallenged;
+} WINHTTP_AUTOPROXY_OPTIONS;
+
+typedef struct {
+ DWORD dwAccessType;
+ LPWSTR lpszProxy;
+ LPWSTR lpszProxyBypass;
+} WINHTTP_PROXY_INFO;
+
+typedef struct {
+ BOOL fAutoDetect;
+ LPWSTR lpszAutoConfigUrl;
+ LPWSTR lpszProxy;
+ LPWSTR lpszProxyBypass;
+} WINHTTP_CURRENT_USER_IE_PROXY_CONFIG;
+
+#define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001
+#define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002
+
+#define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001
+#define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002
+
+#define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0
+#define WINHTTP_ACCESS_TYPE_NO_PROXY 1
+#define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3
+
+#define WINHTTP_NO_PROXY_NAME NULL
+#define WINHTTP_NO_PROXY_BYPASS NULL
+
+#define WINHTTP_ERROR_BASE 12000
+#define ERROR_WINHTTP_LOGIN_FAILURE (WINHTTP_ERROR_BASE + 15)
+#define ERROR_WINHTTP_AUTODETECTION_FAILED (WINHTTP_ERROR_BASE + 180)
+
+QT_BEGIN_NAMESPACE
+
+typedef BOOL (WINAPI * PtrWinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS*, WINHTTP_PROXY_INFO*);
+typedef HINTERNET (WINAPI * PtrWinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR,DWORD);
+typedef BOOL (WINAPI * PtrWinHttpGetDefaultProxyConfiguration)(WINHTTP_PROXY_INFO*);
+typedef BOOL (WINAPI * PtrWinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG*);
+typedef BOOL (WINAPI * PtrWinHttpCloseHandle)(HINTERNET);
+static PtrWinHttpGetProxyForUrl ptrWinHttpGetProxyForUrl = 0;
+static PtrWinHttpOpen ptrWinHttpOpen = 0;
+static PtrWinHttpGetDefaultProxyConfiguration ptrWinHttpGetDefaultProxyConfiguration = 0;
+static PtrWinHttpGetIEProxyConfigForCurrentUser ptrWinHttpGetIEProxyConfigForCurrentUser = 0;
+static PtrWinHttpCloseHandle ptrWinHttpCloseHandle = 0;
+
+
+static QStringList splitSpaceSemicolon(const QString &source)
+{
+ QStringList list;
+ int start = 0;
+ int end;
+ while (true) {
+ int space = source.indexOf(QLatin1Char(' '), start);
+ int semicolon = source.indexOf(QLatin1Char(';'), start);
+ end = space;
+ if (semicolon != -1 && (end == -1 || semicolon < end))
+ end = semicolon;
+
+ if (end == -1) {
+ if (start != source.length())
+ list.append(source.mid(start));
+ return list;
+ }
+ if (start != end)
+ list.append(source.mid(start, end - start));
+ start = end + 1;
+ }
+ return list;
+}
+
+static bool isBypassed(const QString &host, const QStringList &bypassList)
+{
+ if (host.isEmpty())
+ return true;
+
+ bool isSimple = !host.contains(QLatin1Char('.')) && !host.contains(QLatin1Char(':'));
+
+ QHostAddress ipAddress;
+ bool isIpAddress = ipAddress.setAddress(host);
+
+ // does it match the list of exclusions?
+ foreach (const QString &entry, bypassList) {
+ if (isSimple && entry == QLatin1String("<local>"))
+ return true;
+ if (isIpAddress && ipAddress.isInSubnet(QHostAddress::parseSubnet(entry))) {
+ return true; // excluded
+ } else {
+ // do wildcard matching
+ QRegExp rx(entry, Qt::CaseInsensitive, QRegExp::Wildcard);
+ if (rx.exactMatch(host))
+ return true;
+ }
+ }
+
+ // host was not excluded
+ return false;
+}
+
+static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, const QStringList &proxyList)
+{
+ // Reference documentation from Microsoft:
+ // http://msdn.microsoft.com/en-us/library/aa383912(VS.85).aspx
+ //
+ // According to the website, the proxy server list is
+ // one or more of the space- or semicolon-separated strings in the format:
+ // ([<scheme>=][<scheme>"://"]<server>[":"<port>])
+
+ QList<QNetworkProxy> result;
+ foreach (const QString &entry, proxyList) {
+ int server = 0;
+
+ int pos = entry.indexOf(QLatin1Char('='));
+ if (pos != -1) {
+ QStringRef scheme = entry.leftRef(pos);
+ if (scheme != query.protocolTag())
+ continue;
+
+ server = pos + 1;
+ }
+
+ QNetworkProxy::ProxyType proxyType = QNetworkProxy::HttpProxy;
+ quint16 port = 8080;
+
+ pos = entry.indexOf(QLatin1String("://"), server);
+ if (pos != -1) {
+ QStringRef scheme = entry.midRef(server, pos - server);
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
+ // no-op
+ // defaults are above
+ } else if (scheme == QLatin1String("socks") || scheme == QLatin1String("socks5")) {
+ proxyType = QNetworkProxy::Socks5Proxy;
+ port = 1080;
+ } else {
+ // unknown proxy type
+ continue;
+ }
+
+ server = pos + 3;
+ }
+
+ pos = entry.indexOf(QLatin1Char(':'), server);
+ if (pos != -1) {
+ bool ok;
+ uint value = entry.mid(pos + 1).toUInt(&ok);
+ if (!ok || value > 65535)
+ continue; // invalid port number
+
+ port = value;
+ } else {
+ pos = entry.length();
+ }
+
+ result << QNetworkProxy(proxyType, entry.mid(server, pos - server), port);
+ }
+
+ return result;
+}
+
+class QWindowsSystemProxy
+{
+public:
+ QWindowsSystemProxy();
+ ~QWindowsSystemProxy();
+ void init();
+
+ QMutex mutex;
+
+ HINTERNET hHttpSession;
+ WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
+
+ QString autoConfigUrl;
+ QStringList proxyServerList;
+ QStringList proxyBypass;
+ QList<QNetworkProxy> defaultResult;
+
+ bool initialized;
+ bool functional;
+ bool isAutoConfig;
+};
+
+Q_GLOBAL_STATIC(QWindowsSystemProxy, systemProxy)
+
+QWindowsSystemProxy::QWindowsSystemProxy()
+ : initialized(false), functional(false), isAutoConfig(false)
+{
+ defaultResult << QNetworkProxy::NoProxy;
+}
+
+QWindowsSystemProxy::~QWindowsSystemProxy()
+{
+ if (hHttpSession)
+ ptrWinHttpCloseHandle(hHttpSession);
+}
+
+void QWindowsSystemProxy::init()
+{
+ if (initialized)
+ return;
+ initialized = true;
+
+#ifdef Q_OS_WINCE
+ // Windows CE does not have any of the following API
+ return;
+#else
+ // load the winhttp.dll library
+ QSystemLibrary lib(L"winhttp");
+ if (!lib.load())
+ return; // failed to load
+
+ ptrWinHttpOpen = (PtrWinHttpOpen)lib.resolve("WinHttpOpen");
+ ptrWinHttpCloseHandle = (PtrWinHttpCloseHandle)lib.resolve("WinHttpCloseHandle");
+ ptrWinHttpGetProxyForUrl = (PtrWinHttpGetProxyForUrl)lib.resolve("WinHttpGetProxyForUrl");
+ ptrWinHttpGetDefaultProxyConfiguration = (PtrWinHttpGetDefaultProxyConfiguration)lib.resolve("WinHttpGetDefaultProxyConfiguration");
+ ptrWinHttpGetIEProxyConfigForCurrentUser = (PtrWinHttpGetIEProxyConfigForCurrentUser)lib.resolve("WinHttpGetIEProxyConfigForCurrentUser");
+
+ // Try to obtain the Internet Explorer configuration.
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyConfig;
+ if (ptrWinHttpGetIEProxyConfigForCurrentUser(&ieProxyConfig)) {
+ if (ieProxyConfig.lpszAutoConfigUrl) {
+ autoConfigUrl = QString::fromWCharArray(ieProxyConfig.lpszAutoConfigUrl);
+ GlobalFree(ieProxyConfig.lpszAutoConfigUrl);
+ }
+ if (ieProxyConfig.lpszProxy) {
+ // http://msdn.microsoft.com/en-us/library/aa384250%28VS.85%29.aspx speaks only about a "proxy URL",
+ // not multiple URLs. However we tested this and it can return multiple URLs. So we use splitSpaceSemicolon
+ // on it.
+ proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxy));
+ GlobalFree(ieProxyConfig.lpszProxy);
+ }
+ if (ieProxyConfig.lpszProxyBypass) {
+ proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(ieProxyConfig.lpszProxyBypass));
+ GlobalFree(ieProxyConfig.lpszProxyBypass);
+ }
+ }
+
+ hHttpSession = NULL;
+ if (ieProxyConfig.fAutoDetect || !autoConfigUrl.isEmpty()) {
+ // using proxy autoconfiguration
+ proxyServerList.clear();
+ proxyBypass.clear();
+
+ // open the handle and obtain the options
+ hHttpSession = ptrWinHttpOpen(L"Qt System Proxy access/1.0",
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+ if (!hHttpSession)
+ return;
+
+ isAutoConfig = true;
+ memset(&autoProxyOptions, 0, sizeof autoProxyOptions);
+ autoProxyOptions.fAutoLogonIfChallenged = false;
+ if (ieProxyConfig.fAutoDetect) {
+ autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+ autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
+ WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ } else {
+ autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+ autoProxyOptions.lpszAutoConfigUrl = (LPCWSTR)autoConfigUrl.utf16();
+ }
+ } else {
+ // not auto-detected
+ // attempt to get the static configuration instead
+ WINHTTP_PROXY_INFO proxyInfo;
+ if (ptrWinHttpGetDefaultProxyConfiguration(&proxyInfo) &&
+ proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
+ // we got information from the registry
+ // overwrite the IE configuration, if any
+
+ proxyBypass = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxyBypass));
+ proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
+ }
+
+ if (proxyInfo.lpszProxy)
+ GlobalFree(proxyInfo.lpszProxy);
+ if (proxyInfo.lpszProxyBypass)
+ GlobalFree(proxyInfo.lpszProxyBypass);
+ }
+
+ functional = isAutoConfig || !proxyServerList.isEmpty();
+#endif
+}
+
+QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
+{
+ QWindowsSystemProxy *sp = systemProxy();
+ if (!sp)
+ return QList<QNetworkProxy>() << QNetworkProxy();
+
+ QMutexLocker locker(&sp->mutex);
+ sp->init();
+ if (!sp->functional)
+ return sp->defaultResult;
+
+ if (sp->isAutoConfig) {
+ WINHTTP_PROXY_INFO proxyInfo;
+
+ // try to get the proxy config for the URL
+ QUrl url = query.url();
+ // url could be empty, e.g. from QNetworkProxy::applicationProxy(), that's fine,
+ // we'll still ask for the proxy.
+ // But for a file url, we know we don't need one.
+ if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc"))
+ return sp->defaultResult;
+ if (query.queryType() != QNetworkProxyQuery::UrlRequest) {
+ // change the scheme to https, maybe it'll work
+ url.setScheme(QLatin1String("https"));
+ }
+
+ bool getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
+ (LPCWSTR)url.toString().utf16(),
+ &sp->autoProxyOptions,
+ &proxyInfo);
+ DWORD getProxyError = GetLastError();
+
+ if (!getProxySucceeded
+ && (ERROR_WINHTTP_LOGIN_FAILURE == getProxyError)) {
+ // We first tried without AutoLogon, because this might prevent caching the result.
+ // But now we've to enable it (http://msdn.microsoft.com/en-us/library/aa383153%28v=VS.85%29.aspx)
+ sp->autoProxyOptions.fAutoLogonIfChallenged = TRUE;
+ getProxySucceeded = ptrWinHttpGetProxyForUrl(sp->hHttpSession,
+ (LPCWSTR)url.toString().utf16(),
+ &sp->autoProxyOptions,
+ &proxyInfo);
+ getProxyError = GetLastError();
+ }
+
+ if (getProxySucceeded) {
+ // yes, we got a config for this URL
+ QString proxyBypass = QString::fromWCharArray(proxyInfo.lpszProxyBypass);
+ QStringList proxyServerList = splitSpaceSemicolon(QString::fromWCharArray(proxyInfo.lpszProxy));
+ if (proxyInfo.lpszProxy)
+ GlobalFree(proxyInfo.lpszProxy);
+ if (proxyInfo.lpszProxyBypass)
+ GlobalFree(proxyInfo.lpszProxyBypass);
+
+ if (isBypassed(query.peerHostName(), splitSpaceSemicolon(proxyBypass)))
+ return sp->defaultResult;
+ return parseServerList(query, proxyServerList);
+ }
+
+ // GetProxyForUrl failed
+
+ if (ERROR_WINHTTP_AUTODETECTION_FAILED == getProxyError) {
+ //No config file could be retrieved on the network.
+ //Don't search for it next time again.
+ sp->isAutoConfig = false;
+ }
+
+ return sp->defaultResult;
+ }
+
+ // static configuration
+ if (isBypassed(query.peerHostName(), sp->proxyBypass))
+ return sp->defaultResult;
+
+ QList<QNetworkProxy> result = parseServerList(query, sp->proxyServerList);
+ // In some cases, this was empty. See SF task 00062670
+ if (result.isEmpty())
+ return sp->defaultResult;
+
+ return result;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qurlinfo.cpp b/src/network/kernel/qurlinfo.cpp
new file mode 100644
index 0000000000..cff4912904
--- /dev/null
+++ b/src/network/kernel/qurlinfo.cpp
@@ -0,0 +1,731 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qurlinfo.h"
+
+#ifndef QT_NO_URLINFO
+
+#include "qurl.h"
+#include "qdir.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QUrlInfoPrivate
+{
+public:
+ QUrlInfoPrivate() :
+ permissions(0),
+ size(0),
+ isDir(false),
+ isFile(true),
+ isSymLink(false),
+ isWritable(true),
+ isReadable(true),
+ isExecutable(false)
+ {}
+
+ QString name;
+ int permissions;
+ QString owner;
+ QString group;
+ qint64 size;
+
+ QDateTime lastModified;
+ QDateTime lastRead;
+ bool isDir;
+ bool isFile;
+ bool isSymLink;
+ bool isWritable;
+ bool isReadable;
+ bool isExecutable;
+};
+
+
+/*!
+ \class QUrlInfo
+ \brief The QUrlInfo class stores information about URLs.
+
+ \ingroup io
+ \ingroup network
+
+ The information about a URL that can be retrieved includes name(),
+ permissions(), owner(), group(), size(), lastModified(),
+ lastRead(), isDir(), isFile(), isSymLink(), isWritable(),
+ isReadable() and isExecutable().
+
+ You can create your own QUrlInfo objects passing in all the
+ relevant information in the constructor, and you can modify a
+ QUrlInfo; for each getter mentioned above there is an equivalent
+ setter. Note that setting values does not affect the underlying
+ resource that the QUrlInfo provides information about; for example
+ if you call setWritable(true) on a read-only resource the only
+ thing changed is the QUrlInfo object, not the resource.
+
+ \sa QUrl, {FTP Example}
+*/
+
+/*!
+ \enum QUrlInfo::PermissionSpec
+
+ This enum is used by the permissions() function to report the
+ permissions of a file.
+
+ \value ReadOwner The file is readable by the owner of the file.
+ \value WriteOwner The file is writable by the owner of the file.
+ \value ExeOwner The file is executable by the owner of the file.
+ \value ReadGroup The file is readable by the group.
+ \value WriteGroup The file is writable by the group.
+ \value ExeGroup The file is executable by the group.
+ \value ReadOther The file is readable by anyone.
+ \value WriteOther The file is writable by anyone.
+ \value ExeOther The file is executable by anyone.
+*/
+
+/*!
+ Constructs an invalid QUrlInfo object with default values.
+
+ \sa isValid()
+*/
+
+QUrlInfo::QUrlInfo()
+{
+ d = 0;
+}
+
+/*!
+ Copy constructor, copies \a ui to this URL info object.
+*/
+
+QUrlInfo::QUrlInfo(const QUrlInfo &ui)
+{
+ if (ui.d) {
+ d = new QUrlInfoPrivate;
+ *d = *ui.d;
+ } else {
+ d = 0;
+ }
+}
+
+/*!
+ Constructs a QUrlInfo object by specifying all the URL's
+ information.
+
+ The information that is passed is the \a name, file \a
+ permissions, \a owner and \a group and the file's \a size. Also
+ passed is the \a lastModified date/time and the \a lastRead
+ date/time. Flags are also passed, specifically, \a isDir, \a
+ isFile, \a isSymLink, \a isWritable, \a isReadable and \a
+ isExecutable.
+*/
+
+QUrlInfo::QUrlInfo(const QString &name, int permissions, const QString &owner,
+ const QString &group, qint64 size, const QDateTime &lastModified,
+ const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+ bool isWritable, bool isReadable, bool isExecutable)
+{
+ d = new QUrlInfoPrivate;
+ d->name = name;
+ d->permissions = permissions;
+ d->owner = owner;
+ d->group = group;
+ d->size = size;
+ d->lastModified = lastModified;
+ d->lastRead = lastRead;
+ d->isDir = isDir;
+ d->isFile = isFile;
+ d->isSymLink = isSymLink;
+ d->isWritable = isWritable;
+ d->isReadable = isReadable;
+ d->isExecutable = isExecutable;
+}
+
+
+/*!
+ Constructs a QUrlInfo object by specifying all the URL's
+ information.
+
+ The information that is passed is the \a url, file \a
+ permissions, \a owner and \a group and the file's \a size. Also
+ passed is the \a lastModified date/time and the \a lastRead
+ date/time. Flags are also passed, specifically, \a isDir, \a
+ isFile, \a isSymLink, \a isWritable, \a isReadable and \a
+ isExecutable.
+*/
+
+QUrlInfo::QUrlInfo(const QUrl &url, int permissions, const QString &owner,
+ const QString &group, qint64 size, const QDateTime &lastModified,
+ const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+ bool isWritable, bool isReadable, bool isExecutable)
+{
+ d = new QUrlInfoPrivate;
+ d->name = QFileInfo(url.path()).fileName();
+ d->permissions = permissions;
+ d->owner = owner;
+ d->group = group;
+ d->size = size;
+ d->lastModified = lastModified;
+ d->lastRead = lastRead;
+ d->isDir = isDir;
+ d->isFile = isFile;
+ d->isSymLink = isSymLink;
+ d->isWritable = isWritable;
+ d->isReadable = isReadable;
+ d->isExecutable = isExecutable;
+}
+
+
+/*!
+ Sets the name of the URL to \a name. The name is the full text,
+ for example, "http://qt.nokia.com/doc/qurlinfo.html".
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setName(const QString &name)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->name = name;
+}
+
+
+/*!
+ If \a b is true then the URL is set to be a directory; if \a b is
+ false then the URL is set not to be a directory (which normally
+ means it is a file). (Note that a URL can refer to both a file and
+ a directory even though most file systems do not support this.)
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setDir(bool b)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->isDir = b;
+}
+
+
+/*!
+ If \a b is true then the URL is set to be a file; if \b is false
+ then the URL is set not to be a file (which normally means it is a
+ directory). (Note that a URL can refer to both a file and a
+ directory even though most file systems do not support this.)
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setFile(bool b)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->isFile = b;
+}
+
+
+/*!
+ Specifies that the URL refers to a symbolic link if \a b is true
+ and that it does not if \a b is false.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setSymLink(bool b)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->isSymLink = b;
+}
+
+
+/*!
+ Specifies that the URL is writable if \a b is true and not
+ writable if \a b is false.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setWritable(bool b)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->isWritable = b;
+}
+
+
+/*!
+ Specifies that the URL is readable if \a b is true and not
+ readable if \a b is false.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setReadable(bool b)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->isReadable = b;
+}
+
+/*!
+ Specifies that the owner of the URL is called \a s.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setOwner(const QString &s)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->owner = s;
+}
+
+/*!
+ Specifies that the owning group of the URL is called \a s.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setGroup(const QString &s)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->group = s;
+}
+
+/*!
+ Specifies the \a size of the URL.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setSize(qint64 size)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->size = size;
+}
+
+/*!
+ Specifies that the URL has access permissions \a p.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setPermissions(int p)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->permissions = p;
+}
+
+/*!
+ Specifies that the object the URL refers to was last modified at
+ \a dt.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setLastModified(const QDateTime &dt)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->lastModified = dt;
+}
+
+/*!
+ \since 4.4
+
+ Specifies that the object the URL refers to was last read at
+ \a dt.
+
+ If you call this function for an invalid URL info, this function
+ turns it into a valid one.
+
+ \sa isValid()
+*/
+
+void QUrlInfo::setLastRead(const QDateTime &dt)
+{
+ if (!d)
+ d = new QUrlInfoPrivate;
+ d->lastRead = dt;
+}
+
+/*!
+ Destroys the URL info object.
+*/
+
+QUrlInfo::~QUrlInfo()
+{
+ delete d;
+}
+
+/*!
+ Assigns the values of \a ui to this QUrlInfo object.
+*/
+
+QUrlInfo &QUrlInfo::operator=(const QUrlInfo &ui)
+{
+ if (ui.d) {
+ if (!d)
+ d= new QUrlInfoPrivate;
+ *d = *ui.d;
+ } else {
+ delete d;
+ d = 0;
+ }
+ return *this;
+}
+
+/*!
+ Returns the file name of the URL.
+
+ \sa isValid()
+*/
+
+QString QUrlInfo::name() const
+{
+ if (!d)
+ return QString();
+ return d->name;
+}
+
+/*!
+ Returns the permissions of the URL. You can use the \c PermissionSpec flags
+ to test for certain permissions.
+
+ \sa isValid()
+*/
+
+int QUrlInfo::permissions() const
+{
+ if (!d)
+ return 0;
+ return d->permissions;
+}
+
+/*!
+ Returns the owner of the URL.
+
+ \sa isValid()
+*/
+
+QString QUrlInfo::owner() const
+{
+ if (!d)
+ return QString();
+ return d->owner;
+}
+
+/*!
+ Returns the group of the URL.
+
+ \sa isValid()
+*/
+
+QString QUrlInfo::group() const
+{
+ if (!d)
+ return QString();
+ return d->group;
+}
+
+/*!
+ Returns the size of the URL.
+
+ \sa isValid()
+*/
+
+qint64 QUrlInfo::size() const
+{
+ if (!d)
+ return 0;
+ return d->size;
+}
+
+/*!
+ Returns the last modification date of the URL.
+
+ \sa isValid()
+*/
+
+QDateTime QUrlInfo::lastModified() const
+{
+ if (!d)
+ return QDateTime();
+ return d->lastModified;
+}
+
+/*!
+ Returns the date when the URL was last read.
+
+ \sa isValid()
+*/
+
+QDateTime QUrlInfo::lastRead() const
+{
+ if (!d)
+ return QDateTime();
+ return d->lastRead;
+}
+
+/*!
+ Returns true if the URL is a directory; otherwise returns false.
+
+ \sa isValid()
+*/
+
+bool QUrlInfo::isDir() const
+{
+ if (!d)
+ return false;
+ return d->isDir;
+}
+
+/*!
+ Returns true if the URL is a file; otherwise returns false.
+
+ \sa isValid()
+*/
+
+bool QUrlInfo::isFile() const
+{
+ if (!d)
+ return false;
+ return d->isFile;
+}
+
+/*!
+ Returns true if the URL is a symbolic link; otherwise returns false.
+
+ \sa isValid()
+*/
+
+bool QUrlInfo::isSymLink() const
+{
+ if (!d)
+ return false;
+ return d->isSymLink;
+}
+
+/*!
+ Returns true if the URL is writable; otherwise returns false.
+
+ \sa isValid()
+*/
+
+bool QUrlInfo::isWritable() const
+{
+ if (!d)
+ return false;
+ return d->isWritable;
+}
+
+/*!
+ Returns true if the URL is readable; otherwise returns false.
+
+ \sa isValid()
+*/
+
+bool QUrlInfo::isReadable() const
+{
+ if (!d)
+ return false;
+ return d->isReadable;
+}
+
+/*!
+ Returns true if the URL is executable; otherwise returns false.
+
+ \sa isValid()
+*/
+
+bool QUrlInfo::isExecutable() const
+{
+ if (!d)
+ return false;
+ return d->isExecutable;
+}
+
+/*!
+ Returns true if \a i1 is greater than \a i2; otherwise returns
+ false. The objects are compared by the value, which is specified
+ by \a sortBy. This must be one of QDir::Name, QDir::Time or
+ QDir::Size.
+*/
+
+bool QUrlInfo::greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,
+ int sortBy)
+{
+ switch (sortBy) {
+ case QDir::Name:
+ return i1.name() > i2.name();
+ case QDir::Time:
+ return i1.lastModified() > i2.lastModified();
+ case QDir::Size:
+ return i1.size() > i2.size();
+ default:
+ return false;
+ }
+}
+
+/*!
+ Returns true if \a i1 is less than \a i2; otherwise returns false.
+ The objects are compared by the value, which is specified by \a
+ sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size.
+*/
+
+bool QUrlInfo::lessThan(const QUrlInfo &i1, const QUrlInfo &i2,
+ int sortBy)
+{
+ return !greaterThan(i1, i2, sortBy);
+}
+
+/*!
+ Returns true if \a i1 equals to \a i2; otherwise returns false.
+ The objects are compared by the value, which is specified by \a
+ sortBy. This must be one of QDir::Name, QDir::Time or QDir::Size.
+*/
+
+bool QUrlInfo::equal(const QUrlInfo &i1, const QUrlInfo &i2,
+ int sortBy)
+{
+ switch (sortBy) {
+ case QDir::Name:
+ return i1.name() == i2.name();
+ case QDir::Time:
+ return i1.lastModified() == i2.lastModified();
+ case QDir::Size:
+ return i1.size() == i2.size();
+ default:
+ return false;
+ }
+}
+
+/*!
+ Returns true if this QUrlInfo is equal to \a other; otherwise
+ returns false.
+
+ \sa lessThan(), equal()
+*/
+
+bool QUrlInfo::operator==(const QUrlInfo &other) const
+{
+ if (!d)
+ return other.d == 0;
+ if (!other.d)
+ return false;
+
+ return (d->name == other.d->name &&
+ d->permissions == other.d->permissions &&
+ d->owner == other.d->owner &&
+ d->group == other.d->group &&
+ d->size == other.d->size &&
+ d->lastModified == other.d->lastModified &&
+ d->lastRead == other.d->lastRead &&
+ d->isDir == other.d->isDir &&
+ d->isFile == other.d->isFile &&
+ d->isSymLink == other.d->isSymLink &&
+ d->isWritable == other.d->isWritable &&
+ d->isReadable == other.d->isReadable &&
+ d->isExecutable == other.d->isExecutable);
+}
+
+/*!
+ \fn bool QUrlInfo::operator!=(const QUrlInfo &other) const
+ \since 4.2
+
+ Returns true if this QUrlInfo is not equal to \a other; otherwise
+ returns false.
+
+ \sa lessThan(), equal()
+*/
+
+/*!
+ Returns true if the URL info is valid; otherwise returns false.
+ Valid means that the QUrlInfo contains real information.
+
+ You should always check if the URL info is valid before relying on
+ the values.
+*/
+bool QUrlInfo::isValid() const
+{
+ return d != 0;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_URLINFO
diff --git a/src/network/kernel/qurlinfo.h b/src/network/kernel/qurlinfo.h
new file mode 100644
index 0000000000..d40bf0c44d
--- /dev/null
+++ b/src/network/kernel/qurlinfo.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QURLINFO_H
+#define QURLINFO_H
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qiodevice.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_URLINFO
+
+class QUrl;
+class QUrlInfoPrivate;
+
+class Q_NETWORK_EXPORT QUrlInfo
+{
+public:
+ enum PermissionSpec {
+ ReadOwner = 00400, WriteOwner = 00200, ExeOwner = 00100,
+ ReadGroup = 00040, WriteGroup = 00020, ExeGroup = 00010,
+ ReadOther = 00004, WriteOther = 00002, ExeOther = 00001 };
+
+ QUrlInfo();
+ QUrlInfo(const QUrlInfo &ui);
+ QUrlInfo(const QString &name, int permissions, const QString &owner,
+ const QString &group, qint64 size, const QDateTime &lastModified,
+ const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+ bool isWritable, bool isReadable, bool isExecutable);
+ QUrlInfo(const QUrl &url, int permissions, const QString &owner,
+ const QString &group, qint64 size, const QDateTime &lastModified,
+ const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,
+ bool isWritable, bool isReadable, bool isExecutable);
+ QUrlInfo &operator=(const QUrlInfo &ui);
+ virtual ~QUrlInfo();
+
+ virtual void setName(const QString &name);
+ virtual void setDir(bool b);
+ virtual void setFile(bool b);
+ virtual void setSymLink(bool b);
+ virtual void setOwner(const QString &s);
+ virtual void setGroup(const QString &s);
+ virtual void setSize(qint64 size);
+ virtual void setWritable(bool b);
+ virtual void setReadable(bool b);
+ virtual void setPermissions(int p);
+ virtual void setLastModified(const QDateTime &dt);
+ void setLastRead(const QDateTime &dt);
+
+ bool isValid() const;
+
+ QString name() const;
+ int permissions() const;
+ QString owner() const;
+ QString group() const;
+ qint64 size() const;
+ QDateTime lastModified() const;
+ QDateTime lastRead() const;
+ bool isDir() const;
+ bool isFile() const;
+ bool isSymLink() const;
+ bool isWritable() const;
+ bool isReadable() const;
+ bool isExecutable() const;
+
+ static bool greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,
+ int sortBy);
+ static bool lessThan(const QUrlInfo &i1, const QUrlInfo &i2,
+ int sortBy);
+ static bool equal(const QUrlInfo &i1, const QUrlInfo &i2,
+ int sortBy);
+
+ bool operator==(const QUrlInfo &i) const;
+ inline bool operator!=(const QUrlInfo &i) const
+ { return !operator==(i); }
+
+private:
+ QUrlInfoPrivate *d;
+};
+
+#endif // QT_NO_URLINFO
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QURLINFO_H
diff --git a/src/network/network.pro b/src/network/network.pro
new file mode 100644
index 0000000000..948922b8c1
--- /dev/null
+++ b/src/network/network.pro
@@ -0,0 +1,31 @@
+# Qt network module
+
+TARGET = QtNetwork
+QPRO_PWD = $$PWD
+DEFINES += QT_BUILD_NETWORK_LIB QT_NO_USING_NAMESPACE
+#DEFINES += QLOCALSERVER_DEBUG QLOCALSOCKET_DEBUG
+#DEFINES += QNETWORKDISKCACHE_DEBUG
+#DEFINES += QSSLSOCKET_DEBUG
+#DEFINES += QHOSTINFO_DEBUG
+#DEFINES += QABSTRACTSOCKET_DEBUG QNATIVESOCKETENGINE_DEBUG
+#DEFINES += QTCPSOCKETENGINE_DEBUG QTCPSOCKET_DEBUG QTCPSERVER_DEBUG QSSLSOCKET_DEBUG
+#DEFINES += QUDPSOCKET_DEBUG QUDPSERVER_DEBUG
+QT = core
+win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x64000000
+
+unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore
+
+include(../qbase.pri)
+include(access/access.pri)
+include(bearer/bearer.pri)
+include(kernel/kernel.pri)
+include(socket/socket.pri)
+include(ssl/ssl.pri)
+
+QMAKE_LIBS += $$QMAKE_LIBS_NETWORK
+
+
+symbian {
+ TARGET.UID3=0x2001B2DE
+ LIBS += -lesock -linsock -lcertstore -lefsrv -lctframework
+}
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
new file mode 100644
index 0000000000..7af71ccc8b
--- /dev/null
+++ b/src/network/socket/qabstractsocket.cpp
@@ -0,0 +1,2920 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QABSTRACTSOCKET_DEBUG
+
+/*!
+ \class QAbstractSocket
+
+ \brief The QAbstractSocket class provides the base functionality
+ common to all socket types.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ QAbstractSocket is the base class for QTcpSocket and QUdpSocket
+ and contains all common functionality of these two classes. If
+ you need a socket, you have two options:
+
+ \list
+ \i Instantiate QTcpSocket or QUdpSocket.
+ \i Create a native socket descriptor, instantiate
+ QAbstractSocket, and call setSocketDescriptor() to wrap the
+ native socket.
+ \endlist
+
+ TCP (Transmission Control Protocol) is a reliable,
+ stream-oriented, connection-oriented transport protocol. UDP
+ (User Datagram Protocol) is an unreliable, datagram-oriented,
+ connectionless protocol. In practice, this means that TCP is
+ better suited for continuous transmission of data, whereas the
+ more lightweight UDP can be used when reliability isn't
+ important.
+
+ QAbstractSocket's API unifies most of the differences between the
+ two protocols. For example, although UDP is connectionless,
+ connectToHost() establishes a virtual connection for UDP sockets,
+ enabling you to use QAbstractSocket in more or less the same way
+ regardless of the underlying protocol. Internally,
+ QAbstractSocket remembers the address and port passed to
+ connectToHost(), and functions like read() and write() use these
+ values.
+
+ At any time, QAbstractSocket has a state (returned by
+ state()). The initial state is UnconnectedState. After
+ calling connectToHost(), the socket first enters
+ HostLookupState. If the host is found, QAbstractSocket enters
+ ConnectingState and emits the hostFound() signal. When the
+ connection has been established, it enters ConnectedState and
+ emits connected(). If an error occurs at any stage, error() is
+ emitted. Whenever the state changes, stateChanged() is emitted.
+ For convenience, isValid() returns true if the socket is ready for
+ reading and writing, but note that the socket's state must be
+ ConnectedState before reading and writing can occur.
+
+ Read or write data by calling read() or write(), or use the
+ convenience functions readLine() and readAll(). QAbstractSocket
+ also inherits getChar(), putChar(), and ungetChar() from
+ QIODevice, which work on single bytes. The bytesWritten() signal
+ is emitted when data has been written to the socket (i.e., when
+ the client has read the data). Note that Qt does not limit the
+ write buffer size. You can monitor its size by listening to this
+ signal.
+
+ The readyRead() signal is emitted every time a new chunk of data
+ has arrived. bytesAvailable() then returns the number of bytes
+ that are available for reading. Typically, you would connect the
+ readyRead() signal to a slot and read all available data there.
+ If you don't read all the data at once, the remaining data will
+ still be available later, and any new incoming data will be
+ appended to QAbstractSocket's internal read buffer. To limit the
+ size of the read buffer, call setReadBufferSize().
+
+ To close the socket, call disconnectFromHost(). QAbstractSocket enters
+ QAbstractSocket::ClosingState. After all pending data has been written to
+ the socket, QAbstractSocket actually closes the socket, enters
+ QAbstractSocket::ClosedState, and emits disconnected(). If you want to
+ abort a connection immediately, discarding all pending data, call abort()
+ instead. If the remote host closes the connection, QAbstractSocket will
+ emit error(QAbstractSocket::RemoteHostClosedError), during which the socket
+ state will still be ConnectedState, and then the disconnected() signal
+ will be emitted.
+
+ The port and address of the connected peer is fetched by calling
+ peerPort() and peerAddress(). peerName() returns the host name of
+ the peer, as passed to connectToHost(). localPort() and
+ localAddress() return the port and address of the local socket.
+
+ QAbstractSocket provides a set of functions that suspend the
+ calling thread until certain signals are emitted. These functions
+ can be used to implement blocking sockets:
+
+ \list
+ \o waitForConnected() blocks until a connection has been established.
+
+ \o waitForReadyRead() blocks until new data is available for
+ reading.
+
+ \o waitForBytesWritten() blocks until one payload of data has been
+ written to the socket.
+
+ \o waitForDisconnected() blocks until the connection has closed.
+ \endlist
+
+ We show an example:
+
+ \snippet doc/src/snippets/network/tcpwait.cpp 0
+
+ If \l{QIODevice::}{waitForReadyRead()} returns false, the
+ connection has been closed or an error has occurred.
+
+ Programming with a blocking socket is radically different from
+ programming with a non-blocking socket. A blocking socket doesn't
+ require an event loop and typically leads to simpler code.
+ However, in a GUI application, blocking sockets should only be
+ used in non-GUI threads, to avoid freezing the user interface.
+ See the \l network/fortuneclient and \l network/blockingfortuneclient
+ examples for an overview of both approaches.
+
+ \note We discourage the use of the blocking functions together
+ with signals. One of the two possibilities should be used.
+
+ QAbstractSocket can be used with QTextStream and QDataStream's
+ stream operators (operator<<() and operator>>()). There is one
+ issue to be aware of, though: You must make sure that enough data
+ is available before attempting to read it using operator>>().
+
+ \sa QFtp, QNetworkAccessManager, QTcpServer
+*/
+
+/*!
+ \fn void QAbstractSocket::hostFound()
+
+ This signal is emitted after connectToHost() has been called and
+ the host lookup has succeeded.
+
+ \note Since Qt 4.6.3 QAbstractSocket may emit hostFound()
+ directly from the connectToHost() call since a DNS result could have been
+ cached.
+
+ \sa connected()
+*/
+
+/*!
+ \fn void QAbstractSocket::connected()
+
+ This signal is emitted after connectToHost() has been called and
+ a connection has been successfully established.
+
+ \note On some operating systems the connected() signal may
+ be directly emitted from the connectToHost() call for connections
+ to the localhost.
+
+ \sa connectToHost(), disconnected()
+*/
+
+/*!
+ \fn void QAbstractSocket::disconnected()
+
+ This signal is emitted when the socket has been disconnected.
+
+ \warning If you need to delete the sender() of this signal in a slot connected
+ to it, use the \l{QObject::deleteLater()}{deleteLater()} function.
+
+ \sa connectToHost(), disconnectFromHost(), abort()
+*/
+
+/*!
+ \fn void QAbstractSocket::error(QAbstractSocket::SocketError socketError)
+
+ This signal is emitted after an error occurred. The \a socketError
+ parameter describes the type of error that occurred.
+
+ QAbstractSocket::SocketError is not a registered metatype, so for queued
+ connections, you will have to register it with Q_DECLARE_METATYPE() and
+ qRegisterMetaType().
+
+ \sa error(), errorString(), {Creating Custom Qt Types}
+*/
+
+/*!
+ \fn void QAbstractSocket::stateChanged(QAbstractSocket::SocketState socketState)
+
+ This signal is emitted whenever QAbstractSocket's state changes.
+ The \a socketState parameter is the new state.
+
+ QAbstractSocket::SocketState is not a registered metatype, so for queued
+ connections, you will have to register it with Q_REGISTER_METATYPE() and
+ qRegisterMetaType().
+
+ \sa state(), {Creating Custom Qt Types}
+*/
+
+/*!
+ \fn void QAbstractSocket::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
+ \since 4.3
+
+ This signal can be emitted when a \a proxy that requires
+ authentication is used. The \a authenticator object can then be
+ filled in with the required details to allow authentication and
+ continue the connection.
+
+ \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 QAuthenticator, QNetworkProxy
+*/
+
+/*!
+ \enum QAbstractSocket::NetworkLayerProtocol
+
+ This enum describes the network layer protocol values used in Qt.
+
+ \value IPv4Protocol IPv4
+ \value IPv6Protocol IPv6
+ \value UnknownNetworkLayerProtocol Other than IPv4 and IPv6
+
+ \sa QHostAddress::protocol()
+*/
+
+/*!
+ \enum QAbstractSocket::SocketType
+
+ This enum describes the transport layer protocol.
+
+ \value TcpSocket TCP
+ \value UdpSocket UDP
+ \value UnknownSocketType Other than TCP and UDP
+
+ \sa QAbstractSocket::socketType()
+*/
+
+/*!
+ \enum QAbstractSocket::SocketError
+
+ This enum describes the socket errors that can occur.
+
+ \value ConnectionRefusedError The connection was refused by the
+ peer (or timed out).
+ \value RemoteHostClosedError The remote host closed the
+ connection. Note that the client socket (i.e., this socket)
+ will be closed after the remote close notification has
+ been sent.
+ \value HostNotFoundError The host address was not found.
+ \value SocketAccessError The socket operation failed because the
+ application lacked the required privileges.
+ \value SocketResourceError The local system ran out of resources
+ (e.g., too many sockets).
+ \value SocketTimeoutError The socket operation timed out.
+ \value DatagramTooLargeError The datagram was larger than the
+ operating system's limit (which can be as low as 8192
+ bytes).
+ \value NetworkError An error occurred with the network (e.g., the
+ network cable was accidentally plugged out).
+ \value AddressInUseError The address specified to QUdpSocket::bind() is
+ already in use and was set to be exclusive.
+ \value SocketAddressNotAvailableError The address specified to
+ QUdpSocket::bind() does not belong to the host.
+ \value UnsupportedSocketOperationError The requested socket operation is
+ not supported by the local operating system (e.g., lack of
+ IPv6 support).
+ \value ProxyAuthenticationRequiredError The socket is using a proxy, and
+ the proxy requires authentication.
+ \value SslHandshakeFailedError The SSL/TLS handshake failed, so
+ the connection was closed (only used in QSslSocket)
+ \value UnfinishedSocketOperationError Used by QAbstractSocketEngine only,
+ The last operation attempted has not finished yet (still in progress in
+ the background).
+ \value ProxyConnectionRefusedError Could not contact the proxy server because
+ the connection to that server was denied
+ \value ProxyConnectionClosedError The connection to the proxy server was closed
+ unexpectedly (before the connection to the final peer was established)
+ \value ProxyConnectionTimeoutError The connection to the proxy server timed out
+ or the proxy server stopped responding in the authentication phase.
+ \value ProxyNotFoundError The proxy address set with setProxy() (or the application
+ proxy) was not found.
+ \value ProxyProtocolError The connection negotiation with the proxy server
+ because the response from the proxy server could not be understood.
+
+ \value UnknownSocketError An unidentified error occurred.
+ \sa QAbstractSocket::error()
+*/
+
+/*!
+ \enum QAbstractSocket::SocketState
+
+ This enum describes the different states in which a socket can be.
+
+ \value UnconnectedState The socket is not connected.
+ \value HostLookupState The socket is performing a host name lookup.
+ \value ConnectingState The socket has started establishing a connection.
+ \value ConnectedState A connection is established.
+ \value BoundState The socket is bound to an address and port (for servers).
+ \value ClosingState The socket is about to close (data may still
+ be waiting to be written).
+ \value ListeningState For internal use only.
+ \omitvalue Idle
+ \omitvalue HostLookup
+ \omitvalue Connecting
+ \omitvalue Connected
+ \omitvalue Closing
+ \omitvalue Connection
+
+ \sa QAbstractSocket::state()
+*/
+
+/*!
+ \enum QAbstractSocket::SocketOption
+ \since 4.6
+
+ This enum represents the options that can be set on a socket.
+ If desired, they can be set after having received the connected() signal from
+ the socket or after having received a new socket from a QTcpServer.
+
+ \value LowDelayOption Try to optimize the socket for low latency. For a QTcpSocket
+ this would set the TCP_NODELAY option and disable Nagle's algorithm. Set this to 1
+ 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()
+*/
+
+#include "qabstractsocket.h"
+#include "qabstractsocket_p.h"
+
+#include "private/qhostinfo_p.h"
+#include "private/qnetworksession_p.h"
+
+#include <qabstracteventdispatcher.h>
+#include <qhostaddress.h>
+#include <qhostinfo.h>
+#include <qmetaobject.h>
+#include <qpointer.h>
+#include <qtimer.h>
+#include <qelapsedtimer.h>
+#include <qscopedvaluerollback.h>
+
+#ifndef QT_NO_OPENSSL
+#include <QtNetwork/qsslsocket.h>
+#endif
+
+#include <private/qthread_p.h>
+
+#ifdef QABSTRACTSOCKET_DEBUG
+#include <qdebug.h>
+#endif
+
+#include <time.h>
+
+#define Q_CHECK_SOCKETENGINE(returnValue) do { \
+ if (!d->socketEngine) { \
+ return returnValue; \
+ } } while (0)
+
+#ifndef QABSTRACTSOCKET_BUFFERSIZE
+#define QABSTRACTSOCKET_BUFFERSIZE 32768
+#endif
+#define QT_CONNECT_TIMEOUT 30000
+#define QT_TRANSFER_TIMEOUT 120000
+
+QT_BEGIN_NAMESPACE
+
+#if defined QABSTRACTSOCKET_DEBUG
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <qstring.h>
+#include <ctype.h>
+QT_END_INCLUDE_NAMESPACE
+
+/*
+ Returns a human readable representation of the first \a len
+ characters in \a data.
+*/
+static QByteArray qt_prettyDebug(const char *data, int len, int maxLength)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len; ++i) {
+ char c = data[i];
+ if (isprint(int(uchar(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 < maxLength)
+ out += "...";
+
+ return out;
+}
+#endif
+
+static bool isProxyError(QAbstractSocket::SocketError error)
+{
+ switch (error) {
+ case QAbstractSocket::ProxyAuthenticationRequiredError:
+ case QAbstractSocket::ProxyConnectionRefusedError:
+ case QAbstractSocket::ProxyConnectionClosedError:
+ case QAbstractSocket::ProxyConnectionTimeoutError:
+ case QAbstractSocket::ProxyNotFoundError:
+ case QAbstractSocket::ProxyProtocolError:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*! \internal
+
+ Constructs a QAbstractSocketPrivate. Initializes all members.
+*/
+QAbstractSocketPrivate::QAbstractSocketPrivate()
+ : readSocketNotifierCalled(false),
+ readSocketNotifierState(false),
+ readSocketNotifierStateSet(false),
+ emittedReadyRead(false),
+ emittedBytesWritten(false),
+ abortCalled(false),
+ closeCalled(false),
+ pendingClose(false),
+ port(0),
+ localPort(0),
+ peerPort(0),
+ socketEngine(0),
+ cachedSocketDescriptor(-1),
+ readBufferMaxSize(0),
+ readBuffer(QABSTRACTSOCKET_BUFFERSIZE),
+ writeBuffer(QABSTRACTSOCKET_BUFFERSIZE),
+ isBuffered(false),
+ blockingTimeout(30000),
+ connectTimer(0),
+ disconnectTimer(0),
+ connectTimeElapsed(0),
+ hostLookupId(-1),
+ socketType(QAbstractSocket::UnknownSocketType),
+ state(QAbstractSocket::UnconnectedState),
+ socketError(QAbstractSocket::UnknownSocketError)
+{
+}
+
+/*! \internal
+
+ Destructs the QAbstractSocket. If the socket layer is open, it
+ will be reset.
+*/
+QAbstractSocketPrivate::~QAbstractSocketPrivate()
+{
+}
+
+/*! \internal
+
+ Resets the socket layer, clears the read and write buffers and
+ deletes any socket notifiers.
+*/
+void QAbstractSocketPrivate::resetSocketLayer()
+{
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::resetSocketLayer()");
+#endif
+
+ if (socketEngine) {
+ socketEngine->close();
+ socketEngine->disconnect();
+ delete socketEngine;
+ socketEngine = 0;
+ cachedSocketDescriptor = -1;
+ }
+ if (connectTimer)
+ connectTimer->stop();
+ if (disconnectTimer)
+ disconnectTimer->stop();
+}
+
+/*! \internal
+
+ Initializes the socket layer to by of type \a type, using the
+ network layer protocol \a protocol. Resets the socket layer first
+ if it's already initialized. Sets up the socket notifiers.
+*/
+bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtocol protocol)
+{
+#ifdef QT_NO_NETWORKPROXY
+ // this is here to avoid a duplication of the call to createSocketEngine below
+ static const QNetworkProxy &proxyInUse = *(QNetworkProxy *)0;
+#endif
+
+ Q_Q(QAbstractSocket);
+#if defined (QABSTRACTSOCKET_DEBUG)
+ QString typeStr;
+ if (q->socketType() == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
+ else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
+ else typeStr = QLatin1String("UnknownSocketType");
+ QString protocolStr;
+ if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
+ else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol");
+ else protocolStr = QLatin1String("UnknownNetworkLayerProtocol");
+#endif
+
+ resetSocketLayer();
+ socketEngine = QAbstractSocketEngine::createSocketEngine(q->socketType(), proxyInUse, q);
+#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
+ if (!socketEngine) {
+ socketError = QAbstractSocket::UnsupportedSocketOperationError;
+ q->setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));
+ return false;
+ }
+ if (!socketEngine->initialize(q->socketType(), protocol)) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::initSocketLayer(%s, %s) failed (%s)",
+ typeStr.toLatin1().constData(), protocolStr.toLatin1().constData(),
+ socketEngine->errorString().toLatin1().constData());
+#endif
+ socketError = socketEngine->error();
+ q->setErrorString(socketEngine->errorString());
+ return false;
+ }
+
+ if (threadData->eventDispatcher)
+ socketEngine->setReceiver(this);
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::initSocketLayer(%s, %s) success",
+ typeStr.toLatin1().constData(), protocolStr.toLatin1().constData());
+#endif
+ return true;
+}
+
+/*! \internal
+
+ Slot connected to the read socket notifier. This slot is called
+ when new data is available for reading, or when the socket has
+ been closed. Handles recursive calls.
+*/
+bool QAbstractSocketPrivate::canReadNotification()
+{
+ Q_Q(QAbstractSocket);
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::canReadNotification()");
+#endif
+
+ // Prevent recursive calls
+ if (readSocketNotifierCalled) {
+ if (!readSocketNotifierStateSet) {
+ readSocketNotifierStateSet = true;
+ readSocketNotifierState = socketEngine->isReadNotificationEnabled();
+ socketEngine->setReadNotificationEnabled(false);
+ }
+ }
+ QScopedValueRollback<bool> rsncrollback(readSocketNotifierCalled);
+ readSocketNotifierCalled = true;
+
+ if (!isBuffered)
+ socketEngine->setReadNotificationEnabled(false);
+
+ // If buffered, read data from the socket into the read buffer
+ qint64 newBytes = 0;
+ if (isBuffered) {
+ // Return if there is no space in the buffer
+ if (readBufferMaxSize && readBuffer.size() >= readBufferMaxSize) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full");
+#endif
+ return false;
+ }
+
+ // If reading from the socket fails after getting a read
+ // notification, close the socket.
+ newBytes = readBuffer.size();
+ if (!readFromSocket()) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket");
+#endif
+ q->disconnectFromHost();
+ return false;
+ }
+ newBytes = readBuffer.size() - newBytes;
+
+ // If read buffer is full, disable the read socket notifier.
+ if (readBufferMaxSize && readBuffer.size() == readBufferMaxSize) {
+ socketEngine->setReadNotificationEnabled(false);
+ }
+ }
+
+ // only emit readyRead() when not recursing, and only if there is data available
+ bool hasData = newBytes > 0
+#ifndef QT_NO_UDPSOCKET
+ || (!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();
+ }
+
+ // If we were closed as a result of the readyRead() signal,
+ // return.
+ if (state == QAbstractSocket::UnconnectedState || state == QAbstractSocket::ClosingState) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning");
+#endif
+ return true;
+ }
+
+ if (!hasData && socketEngine)
+ socketEngine->setReadNotificationEnabled(true);
+
+ // reset the read socket notifier state if we reentered inside the
+ // readyRead() connected slot.
+ if (readSocketNotifierStateSet && socketEngine &&
+ readSocketNotifierState != socketEngine->isReadNotificationEnabled()) {
+ socketEngine->setReadNotificationEnabled(readSocketNotifierState);
+ readSocketNotifierStateSet = false;
+ }
+ return true;
+}
+
+/*! \internal
+
+ Slot connected to the write socket notifier. It's called during a
+ delayed connect or when the socket is ready for writing.
+*/
+bool QAbstractSocketPrivate::canWriteNotification()
+{
+#if defined (Q_OS_WIN)
+ if (socketEngine && socketEngine->isWriteNotificationEnabled())
+ socketEngine->setWriteNotificationEnabled(false);
+#endif
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::canWriteNotification() flushing");
+#endif
+ int tmp = writeBuffer.size();
+ flush();
+
+ if (socketEngine) {
+#if defined (Q_OS_WIN)
+ if (!writeBuffer.isEmpty())
+ socketEngine->setWriteNotificationEnabled(true);
+#else
+ if (writeBuffer.isEmpty() && socketEngine->bytesToWrite() == 0)
+ socketEngine->setWriteNotificationEnabled(false);
+#endif
+ }
+
+ return (writeBuffer.size() < tmp);
+}
+
+/*! \internal
+
+ Slot connected to a notification of connection status
+ change. Either we finished connecting or we failed to connect.
+*/
+void QAbstractSocketPrivate::connectionNotification()
+{
+ // If in connecting state, check if the connection has been
+ // established, otherwise flush pending data.
+ if (state == QAbstractSocket::ConnectingState) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::connectionNotification() testing connection");
+#endif
+ _q_testConnection();
+ }
+}
+
+/*! \internal
+
+ Writes pending data in the write buffers to the socket. The
+ function writes as much as it can without blocking.
+
+ It is usually invoked by canWriteNotification after one or more
+ calls to write().
+
+ Emits bytesWritten().
+*/
+bool QAbstractSocketPrivate::flush()
+{
+ Q_Q(QAbstractSocket);
+ if (!socketEngine || !socketEngine->isValid() || (writeBuffer.isEmpty()
+ && socketEngine->bytesToWrite() == 0)) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::flush() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s",
+ socketEngine->isValid() ? "yes" : "no", writeBuffer.isEmpty() ? "yes" : "no");
+#endif
+
+ // this covers the case when the buffer was empty, but we had to wait for the socket engine to finish
+ if (state == QAbstractSocket::ClosingState)
+ q->disconnectFromHost();
+
+ return false;
+ }
+
+ int nextSize = writeBuffer.nextDataBlockSize();
+ const char *ptr = writeBuffer.readPointer();
+
+ // Attempt to write it all in one chunk.
+ qint64 written = socketEngine->write(ptr, nextSize);
+ if (written < 0) {
+ socketError = socketEngine->error();
+ q->setErrorString(socketEngine->errorString());
+#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;
+ }
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::flush() %lld bytes written to the network",
+ written);
+#endif
+
+ // Remove what we wrote so far.
+ writeBuffer.free(written);
+ if (written > 0) {
+ // Don't emit bytesWritten() recursively.
+ if (!emittedBytesWritten) {
+ QScopedValueRollback<bool> r(emittedBytesWritten);
+ emittedBytesWritten = true;
+ emit q->bytesWritten(written);
+ }
+ }
+
+ if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()
+ && !socketEngine->bytesToWrite())
+ socketEngine->setWriteNotificationEnabled(false);
+ if (state == QAbstractSocket::ClosingState)
+ q->disconnectFromHost();
+
+ return true;
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*! \internal
+
+ Resolve the proxy to its final value.
+*/
+void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
+{
+ QHostAddress parsed;
+ if (hostname == QLatin1String("localhost")
+ || hostname.startsWith(QLatin1String("localhost."))
+ || (parsed.setAddress(hostname)
+ && (parsed == QHostAddress::LocalHost
+ || parsed == QHostAddress::LocalHostIPv6))) {
+ proxyInUse = QNetworkProxy::NoProxy;
+ return;
+ }
+
+ QList<QNetworkProxy> proxies;
+
+ if (proxy.type() != QNetworkProxy::DefaultProxy) {
+ // a non-default proxy was set with setProxy
+ proxies << proxy;
+ } else {
+ // try the application settings instead
+ QNetworkProxyQuery query(hostname, port, QString(),
+ socketType == QAbstractSocket::TcpSocket ?
+ QNetworkProxyQuery::TcpSocket :
+ QNetworkProxyQuery::UdpSocket);
+ proxies = QNetworkProxyFactory::proxyForQuery(query);
+ }
+
+ // return the first that we can use
+ foreach (const QNetworkProxy &p, proxies) {
+ if (socketType == QAbstractSocket::UdpSocket &&
+ (p.capabilities() & QNetworkProxy::UdpTunnelingCapability) == 0)
+ continue;
+
+ if (socketType == QAbstractSocket::TcpSocket &&
+ (p.capabilities() & QNetworkProxy::TunnelingCapability) == 0)
+ continue;
+
+ proxyInUse = p;
+ return;
+ }
+
+ // no proxy found
+ // DefaultProxy here will raise an error
+ proxyInUse = QNetworkProxy();
+}
+
+/*!
+ \internal
+
+ Starts the connection to \a host, like _q_startConnecting below,
+ but without hostname resolution.
+*/
+void QAbstractSocketPrivate::startConnectingByName(const QString &host)
+{
+ Q_Q(QAbstractSocket);
+ if (state == QAbstractSocket::ConnectingState || state == QAbstractSocket::ConnectedState)
+ return;
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::startConnectingByName(host == %s)", qPrintable(host));
+#endif
+
+ // ### Let the socket engine drive this?
+ state = QAbstractSocket::ConnectingState;
+ emit q->stateChanged(state);
+
+ connectTimeElapsed = 0;
+
+ if (initSocketLayer(QAbstractSocket::UnknownNetworkLayerProtocol)) {
+ if (socketEngine->connectToHostByName(host, port) ||
+ socketEngine->state() == QAbstractSocket::ConnectingState) {
+ cachedSocketDescriptor = socketEngine->socketDescriptor();
+
+ return;
+ }
+
+ // failed to connect
+ socketError = socketEngine->error();
+ q->setErrorString(socketEngine->errorString());
+ }
+
+ state = QAbstractSocket::UnconnectedState;
+ emit q->error(socketError);
+ emit q->stateChanged(state);
+}
+
+#endif
+
+/*! \internal
+
+ Slot connected to QHostInfo::lookupHost() in connectToHost(). This
+ function starts the process of connecting to any number of
+ candidate IP addresses for the host, if it was found. Calls
+ _q_connectToNextAddress().
+*/
+void QAbstractSocketPrivate::_q_startConnecting(const QHostInfo &hostInfo)
+{
+ Q_Q(QAbstractSocket);
+ if (state != QAbstractSocket::HostLookupState)
+ return;
+
+ if (hostLookupId != -1 && hostLookupId != hostInfo.lookupId()) {
+ qWarning("QAbstractSocketPrivate::_q_startConnecting() received hostInfo for wrong lookup ID %d expected %d", hostInfo.lookupId(), hostLookupId);
+ }
+
+ addresses = hostInfo.addresses();
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ QString s = QLatin1String("{");
+ for (int i = 0; i < addresses.count(); ++i) {
+ if (i != 0) s += QLatin1String(", ");
+ s += addresses.at(i).toString();
+ }
+ s += QLatin1Char('}');
+ qDebug("QAbstractSocketPrivate::_q_startConnecting(hostInfo == %s)", s.toLatin1().constData());
+#endif
+
+ // Try all addresses twice.
+ addresses += addresses;
+
+ // If there are no addresses in the host list, report this to the
+ // user.
+ if (addresses.isEmpty()) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_startConnecting(), host not found");
+#endif
+ state = QAbstractSocket::UnconnectedState;
+ socketError = QAbstractSocket::HostNotFoundError;
+ q->setErrorString(QAbstractSocket::tr("Host not found"));
+ emit q->stateChanged(state);
+ emit q->error(QAbstractSocket::HostNotFoundError);
+ return;
+ }
+
+ // Enter Connecting state (see also sn_write, which is called by
+ // the write socket notifier after connect())
+ state = QAbstractSocket::ConnectingState;
+ emit q->stateChanged(state);
+
+ // Report the successful host lookup
+ emit q->hostFound();
+
+ // Reset the total time spent connecting.
+ connectTimeElapsed = 0;
+
+ // The addresses returned by the lookup will be tested one after
+ // another by _q_connectToNextAddress().
+ _q_connectToNextAddress();
+}
+
+/*! \internal
+
+ Called by a queued or direct connection from _q_startConnecting() or
+ _q_testConnection(), this function takes the first address of the
+ pending addresses list and tries to connect to it. If the
+ connection succeeds, QAbstractSocket will emit
+ connected(). Otherwise, error(ConnectionRefusedError) or
+ error(SocketTimeoutError) is emitted.
+*/
+void QAbstractSocketPrivate::_q_connectToNextAddress()
+{
+ Q_Q(QAbstractSocket);
+ do {
+ // Check for more pending addresses
+ if (addresses.isEmpty()) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), all addresses failed.");
+#endif
+ state = QAbstractSocket::UnconnectedState;
+ if (socketEngine) {
+ if ((socketEngine->error() == QAbstractSocket::UnknownSocketError
+#ifdef Q_OS_AIX
+ // On AIX, the second connect call will result in EINVAL and not
+ // ECONNECTIONREFUSED; although the meaning is the same.
+ || socketEngine->error() == QAbstractSocket::UnsupportedSocketOperationError
+#endif
+ ) && socketEngine->state() == QAbstractSocket::ConnectingState) {
+ socketError = QAbstractSocket::ConnectionRefusedError;
+ q->setErrorString(QAbstractSocket::tr("Connection refused"));
+ } else {
+ socketError = socketEngine->error();
+ q->setErrorString(socketEngine->errorString());
+ }
+ } else {
+// socketError = QAbstractSocket::ConnectionRefusedError;
+// q->setErrorString(QAbstractSocket::tr("Connection refused"));
+ }
+ emit q->stateChanged(state);
+ emit q->error(socketError);
+ return;
+ }
+
+ // Pick the first host address candidate
+ host = addresses.takeFirst();
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), connecting to %s:%i, %d left to try",
+ host.toString().toLatin1().constData(), port, addresses.count());
+#endif
+
+#if defined(QT_NO_IPV6)
+ if (host.protocol() == QAbstractSocket::IPv6Protocol) {
+ // If we have no IPv6 support, then we will not be able to
+ // connect. So we just pretend we didn't see this address.
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), skipping IPv6 entry");
+#endif
+ continue;
+ }
+#endif
+
+ if (!initSocketLayer(host.protocol())) {
+ // hope that the next address is better
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), failed to initialize sock layer");
+#endif
+ continue;
+ }
+
+ // Tries to connect to the address. If it succeeds immediately
+ // (localhost address on BSD or any UDP connect), emit
+ // connected() and return.
+ if (socketEngine->connectToHost(host, port)) {
+ //_q_testConnection();
+ fetchConnectionParameters();
+ return;
+ }
+
+ // cache the socket descriptor even if we're not fully connected yet
+ cachedSocketDescriptor = socketEngine->socketDescriptor();
+
+ // Check that we're in delayed connection state. If not, try
+ // the next address
+ if (socketEngine->state() != QAbstractSocket::ConnectingState) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_connectToNextAddress(), connection failed (%s)",
+ socketEngine->errorString().toLatin1().constData());
+#endif
+ continue;
+ }
+
+ // Start the connect timer.
+ if (threadData->eventDispatcher) {
+ if (!connectTimer) {
+ connectTimer = new QTimer(q);
+ QObject::connect(connectTimer, SIGNAL(timeout()),
+ q, SLOT(_q_abortConnectionAttempt()),
+ Qt::DirectConnection);
+ }
+ connectTimer->start(QT_CONNECT_TIMEOUT);
+ }
+
+ // Wait for a write notification that will eventually call
+ // _q_testConnection().
+ socketEngine->setWriteNotificationEnabled(true);
+ break;
+ } while (state != QAbstractSocket::ConnectedState);
+}
+
+/*! \internal
+
+ Tests if a connection has been established. If it has, connected()
+ is emitted. Otherwise, _q_connectToNextAddress() is invoked.
+*/
+void QAbstractSocketPrivate::_q_testConnection()
+{
+ if (socketEngine) {
+ if (threadData->eventDispatcher) {
+ if (connectTimer)
+ connectTimer->stop();
+ }
+
+ if (socketEngine->state() == QAbstractSocket::ConnectedState) {
+ // Fetch the parameters if our connection is completed;
+ // otherwise, fall out and try the next address.
+ fetchConnectionParameters();
+ if (pendingClose) {
+ q_func()->disconnectFromHost();
+ pendingClose = false;
+ }
+ return;
+ }
+
+ // don't retry the other addresses if we had a proxy error
+ if (isProxyError(socketEngine->error()))
+ addresses.clear();
+ }
+
+ if (threadData->eventDispatcher) {
+ if (connectTimer)
+ connectTimer->stop();
+ }
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_testConnection() connection failed,"
+ " checking for alternative addresses");
+#endif
+ _q_connectToNextAddress();
+}
+
+/*! \internal
+
+ This function is called after a certain number of seconds has
+ passed while waiting for a connection. It simply tests the
+ connection, and continues to the next address if the connection
+ failed.
+*/
+void QAbstractSocketPrivate::_q_abortConnectionAttempt()
+{
+ Q_Q(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::_q_abortConnectionAttempt() (timed out)");
+#endif
+ if (socketEngine)
+ socketEngine->setWriteNotificationEnabled(false);
+
+ connectTimer->stop();
+
+ if (addresses.isEmpty()) {
+ state = QAbstractSocket::UnconnectedState;
+ socketError = QAbstractSocket::SocketTimeoutError;
+ q->setErrorString(QAbstractSocket::tr("Connection timed out"));
+ emit q->stateChanged(state);
+ emit q->error(socketError);
+ } else {
+ _q_connectToNextAddress();
+ }
+}
+
+void QAbstractSocketPrivate::_q_forceDisconnect()
+{
+ Q_Q(QAbstractSocket);
+ if (socketEngine && socketEngine->isValid() && state == QAbstractSocket::ClosingState) {
+ socketEngine->close();
+ q->disconnectFromHost();
+ }
+}
+
+/*! \internal
+
+ Reads data from the socket layer into the read buffer. Returns
+ true on success; otherwise false.
+*/
+bool QAbstractSocketPrivate::readFromSocket()
+{
+ Q_Q(QAbstractSocket);
+ // Find how many bytes we can read from the socket layer.
+ qint64 bytesToRead = socketEngine->bytesAvailable();
+ if (bytesToRead == 0) {
+ // Under heavy load, certain conditions can trigger read notifications
+ // for socket notifiers on which there is no activity. If we continue
+ // to read 0 bytes from the socket, we will trigger behavior similar
+ // to that which signals a remote close. When we hit this condition,
+ // we try to read 4k of data from the socket, which will give us either
+ // an EAGAIN/EWOULDBLOCK if the connection is alive (i.e., the remote
+ // host has _not_ disappeared).
+ bytesToRead = 4096;
+ }
+ if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size()))
+ bytesToRead = readBufferMaxSize - readBuffer.size();
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::readFromSocket() about to read %d bytes",
+ int(bytesToRead));
+#endif
+
+ // Read from the socket, store data in the read buffer.
+ char *ptr = readBuffer.reserve(bytesToRead);
+ qint64 readBytes = socketEngine->read(ptr, bytesToRead);
+ if (readBytes == -2) {
+ // No bytes currently available for reading.
+ readBuffer.chop(bytesToRead);
+ return true;
+ }
+ readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes)));
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::readFromSocket() got %d bytes, buffer size = %d",
+ int(readBytes), readBuffer.size());
+#endif
+
+ if (!socketEngine->isValid()) {
+ socketError = socketEngine->error();
+ q->setErrorString(socketEngine->errorString());
+ emit q->error(socketError);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::readFromSocket() read failed: %s",
+ q->errorString().toLatin1().constData());
+#endif
+ resetSocketLayer();
+ return false;
+ }
+
+ return true;
+}
+
+/*! \internal
+
+ Sets up the internal state after the connection has succeeded.
+*/
+void QAbstractSocketPrivate::fetchConnectionParameters()
+{
+ Q_Q(QAbstractSocket);
+
+ peerName = hostName;
+ if (socketEngine) {
+ socketEngine->setReadNotificationEnabled(true);
+ socketEngine->setWriteNotificationEnabled(true);
+ localPort = socketEngine->localPort();
+ peerPort = socketEngine->peerPort();
+ localAddress = socketEngine->localAddress();
+ peerAddress = socketEngine->peerAddress();
+ cachedSocketDescriptor = socketEngine->socketDescriptor();
+ }
+
+ state = QAbstractSocket::ConnectedState;
+ emit q->stateChanged(state);
+ emit q->connected();
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocketPrivate::fetchConnectionParameters() connection to %s:%i established",
+ host.toString().toLatin1().constData(), port);
+#endif
+}
+
+
+void QAbstractSocketPrivate::pauseSocketNotifiers(QAbstractSocket *socket)
+{
+ QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
+ if (!socketEngine)
+ return;
+ socket->d_func()->prePauseReadSocketNotifierState = socketEngine->isReadNotificationEnabled();
+ socket->d_func()->prePauseWriteSocketNotifierState = socketEngine->isWriteNotificationEnabled();
+ socket->d_func()->prePauseExceptionSocketNotifierState = socketEngine->isExceptionNotificationEnabled();
+ socketEngine->setReadNotificationEnabled(false);
+ socketEngine->setWriteNotificationEnabled(false);
+ socketEngine->setExceptionNotificationEnabled(false);
+}
+
+void QAbstractSocketPrivate::resumeSocketNotifiers(QAbstractSocket *socket)
+{
+ QAbstractSocketEngine *socketEngine = socket->d_func()->socketEngine;
+ if (!socketEngine)
+ return;
+ socketEngine->setReadNotificationEnabled(socket->d_func()->prePauseReadSocketNotifierState);
+ socketEngine->setWriteNotificationEnabled(socket->d_func()->prePauseWriteSocketNotifierState);
+ socketEngine->setExceptionNotificationEnabled(socket->d_func()->prePauseExceptionSocketNotifierState);
+}
+
+QAbstractSocketEngine* QAbstractSocketPrivate::getSocketEngine(QAbstractSocket *socket)
+{
+ return socket->d_func()->socketEngine;
+}
+
+
+/*! \internal
+
+ Constructs a new abstract socket of type \a socketType. The \a
+ parent argument is passed to QObject's constructor.
+*/
+QAbstractSocket::QAbstractSocket(SocketType socketType,
+ QAbstractSocketPrivate &dd, QObject *parent)
+ : QIODevice(dd, parent)
+{
+ Q_D(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::QAbstractSocket(%sSocket, QAbstractSocketPrivate == %p, parent == %p)",
+ socketType == TcpSocket ? "Tcp" : socketType == UdpSocket
+ ? "Udp" : "Unknown", &dd, parent);
+#endif
+ d->socketType = socketType;
+}
+
+/*!
+ Creates a new abstract socket of type \a socketType. The \a
+ parent argument is passed to QObject's constructor.
+
+ \sa socketType(), QTcpSocket, QUdpSocket
+*/
+QAbstractSocket::QAbstractSocket(SocketType socketType, QObject *parent)
+ : QIODevice(*new QAbstractSocketPrivate, parent)
+{
+ Q_D(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::QAbstractSocket(%p)", parent);
+#endif
+ d->socketType = socketType;
+}
+
+/*!
+ Destroys the socket.
+*/
+QAbstractSocket::~QAbstractSocket()
+{
+ Q_D(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::~QAbstractSocket()");
+#endif
+ if (d->state != UnconnectedState)
+ abort();
+}
+
+/*!
+ Returns true if the socket is valid and ready for use; otherwise
+ returns false.
+
+ \bold{Note:} The socket's state must be ConnectedState before reading and
+ writing can occur.
+
+ \sa state()
+*/
+bool QAbstractSocket::isValid() const
+{
+ return d_func()->socketEngine ? d_func()->socketEngine->isValid() : isOpen();
+}
+
+/*!
+ Attempts to make a connection to \a hostName on the given \a port.
+
+ The socket is opened in the given \a openMode and first enters
+ HostLookupState, then performs a host name lookup of \a hostName.
+ If the lookup succeeds, hostFound() is emitted and QAbstractSocket
+ enters ConnectingState. It then attempts to connect to the address
+ or addresses returned by the lookup. Finally, if a connection is
+ established, QAbstractSocket enters ConnectedState and
+ emits connected().
+
+ At any point, the socket can emit error() to signal that an error
+ occurred.
+
+ \a hostName may be an IP address in string form (e.g.,
+ "43.195.83.32"), or it may be a host name (e.g.,
+ "example.com"). QAbstractSocket will do a lookup only if
+ required. \a port is in native byte order.
+
+ \sa state(), peerName(), peerAddress(), peerPort(), waitForConnected()
+*/
+void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
+ OpenMode openMode)
+{
+ QMetaObject::invokeMethod(this, "connectToHostImplementation",
+ Qt::DirectConnection,
+ Q_ARG(QString, hostName),
+ Q_ARG(quint16, port),
+ Q_ARG(OpenMode, openMode));
+}
+
+/*!
+ \since 4.1
+
+ Contains the implementation of connectToHost().
+
+ Attempts to make a connection to \a hostName on the given \a
+ port. The socket is opened in the given \a openMode.
+*/
+void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint16 port,
+ OpenMode openMode)
+{
+ Q_D(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::connectToHost(\"%s\", %i, %i)...", qPrintable(hostName), port,
+ (int) openMode);
+#endif
+
+ if (d->state == ConnectedState || d->state == ConnectingState
+ || d->state == ClosingState || d->state == HostLookupState) {
+ qWarning("QAbstractSocket::connectToHost() called when already looking up or connecting/connected to \"%s\"", qPrintable(hostName));
+ return;
+ }
+
+ d->hostName = hostName;
+ d->port = port;
+ d->state = UnconnectedState;
+ d->readBuffer.clear();
+ d->writeBuffer.clear();
+ d->abortCalled = false;
+ d->closeCalled = false;
+ d->pendingClose = false;
+ d->localPort = 0;
+ d->peerPort = 0;
+ d->localAddress.clear();
+ d->peerAddress.clear();
+ d->peerName = hostName;
+ if (d->hostLookupId != -1) {
+ QHostInfo::abortHostLookup(d->hostLookupId);
+ d->hostLookupId = -1;
+ }
+
+#ifndef QT_NO_NETWORKPROXY
+ // Get the proxy information
+ d->resolveProxy(hostName, port);
+ if (d->proxyInUse.type() == QNetworkProxy::DefaultProxy) {
+ // failed to setup the proxy
+ d->socketError = QAbstractSocket::UnsupportedSocketOperationError;
+ setErrorString(QAbstractSocket::tr("Operation on socket is not supported"));
+ emit error(d->socketError);
+ return;
+ }
+#endif
+
+ 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);
+
+ QHostAddress temp;
+ if (temp.setAddress(hostName)) {
+ QHostInfo info;
+ info.setAddresses(QList<QHostAddress>() << temp);
+ d->_q_startConnecting(info);
+#ifndef QT_NO_NETWORKPROXY
+ } else if (d->proxyInUse.capabilities() & QNetworkProxy::HostNameLookupCapability) {
+ // the proxy supports connection by name, so use it
+ d->startConnectingByName(hostName);
+ return;
+#endif
+ } else {
+ if (d->threadData->eventDispatcher) {
+ // this internal API for QHostInfo either immediately gives us the desired
+ // QHostInfo from cache or later calls the _q_startConnecting slot.
+ bool immediateResultValid = false;
+ QHostInfo hostInfo = qt_qhostinfo_lookup(hostName,
+ this,
+ SLOT(_q_startConnecting(QHostInfo)),
+ &immediateResultValid,
+ &d->hostLookupId);
+ if (immediateResultValid) {
+ d->hostLookupId = -1;
+ d->_q_startConnecting(hostInfo);
+ }
+ }
+ }
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::connectToHost(\"%s\", %i) == %s%s", hostName.toLatin1().constData(), port,
+ (d->state == ConnectedState) ? "true" : "false",
+ (d->state == ConnectingState || d->state == HostLookupState)
+ ? " (connection in progress)" : "");
+#endif
+}
+
+/*! \overload
+
+ Attempts to make a connection to \a address on port \a port.
+*/
+void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port,
+ OpenMode openMode)
+{
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::connectToHost([%s], %i, %i)...",
+ address.toString().toLatin1().constData(), port, (int) openMode);
+#endif
+ connectToHost(address.toString(), port, openMode);
+}
+
+/*!
+ Returns the number of bytes that are waiting to be written. The
+ bytes are written when control goes back to the event loop or
+ when flush() is called.
+
+ \sa bytesAvailable(), flush()
+*/
+qint64 QAbstractSocket::bytesToWrite() const
+{
+ Q_D(const QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::bytesToWrite() == %i", d->writeBuffer.size());
+#endif
+ return (qint64)d->writeBuffer.size();
+}
+
+/*!
+ Returns the number of incoming bytes that are waiting to be read.
+
+ \sa bytesToWrite(), read()
+*/
+qint64 QAbstractSocket::bytesAvailable() const
+{
+ Q_D(const QAbstractSocket);
+ qint64 available = QIODevice::bytesAvailable();
+
+ 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
+ return available;
+}
+
+/*!
+ Returns the host port number (in native byte order) of the local
+ socket if available; otherwise returns 0.
+
+ \sa localAddress(), peerPort(), setLocalPort()
+*/
+quint16 QAbstractSocket::localPort() const
+{
+ Q_D(const QAbstractSocket);
+ return d->localPort;
+}
+
+/*!
+ Returns the host address of the local socket if available;
+ otherwise returns QHostAddress::Null.
+
+ This is normally the main IP address of the host, but can be
+ QHostAddress::LocalHost (127.0.0.1) for connections to the
+ local host.
+
+ \sa localPort(), peerAddress(), setLocalAddress()
+*/
+QHostAddress QAbstractSocket::localAddress() const
+{
+ Q_D(const QAbstractSocket);
+ return d->localAddress;
+}
+
+/*!
+ Returns the port of the connected peer if the socket is in
+ ConnectedState; otherwise returns 0.
+
+ \sa peerAddress(), localPort(), setPeerPort()
+*/
+quint16 QAbstractSocket::peerPort() const
+{
+ Q_D(const QAbstractSocket);
+ return d->peerPort;
+}
+
+/*!
+ Returns the address of the connected peer if the socket is in
+ ConnectedState; otherwise returns QHostAddress::Null.
+
+ \sa peerName(), peerPort(), localAddress(), setPeerAddress()
+*/
+QHostAddress QAbstractSocket::peerAddress() const
+{
+ Q_D(const QAbstractSocket);
+ return d->peerAddress;
+}
+
+/*!
+ Returns the name of the peer as specified by connectToHost(), or
+ an empty QString if connectToHost() has not been called.
+
+ \sa peerAddress(), peerPort(), setPeerName()
+*/
+QString QAbstractSocket::peerName() const
+{
+ Q_D(const QAbstractSocket);
+ return d->peerName.isEmpty() ? d->hostName : d->peerName;
+}
+
+/*!
+ Returns true if a line of data can be read from the socket;
+ otherwise returns false.
+
+ \sa readLine()
+*/
+bool QAbstractSocket::canReadLine() const
+{
+ bool hasLine = d_func()->readBuffer.canReadLine();
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::canReadLine() == %s, buffer size = %d, size = %d", hasLine ? "true" : "false",
+ d_func()->readBuffer.size(), d_func()->buffer.size());
+#endif
+ return hasLine || QIODevice::canReadLine();
+}
+
+/*!
+ Returns the native socket descriptor of the QAbstractSocket object
+ if this is available; otherwise returns -1.
+
+ If the socket is using QNetworkProxy, the returned descriptor
+ may not be usable with native socket functions.
+
+ The socket descriptor is not available when QAbstractSocket is in
+ UnconnectedState.
+
+ \sa setSocketDescriptor()
+*/
+int QAbstractSocket::socketDescriptor() const
+{
+ Q_D(const QAbstractSocket);
+ return d->cachedSocketDescriptor;
+}
+
+/*!
+ Initializes QAbstractSocket with the native socket descriptor \a
+ socketDescriptor. Returns true if \a socketDescriptor is accepted
+ as a valid socket descriptor; otherwise returns false.
+ The socket is opened in the mode specified by \a openMode, and
+ enters the socket state specified by \a socketState.
+
+ \bold{Note:} It is not possible to initialize two abstract sockets
+ with the same native socket descriptor.
+
+ \sa socketDescriptor()
+*/
+bool QAbstractSocket::setSocketDescriptor(int socketDescriptor, SocketState socketState,
+ OpenMode openMode)
+{
+ Q_D(QAbstractSocket);
+#ifndef QT_NO_OPENSSL
+ if (QSslSocket *socket = qobject_cast<QSslSocket *>(this))
+ return socket->setSocketDescriptor(socketDescriptor, socketState, openMode);
+#endif
+
+ d->resetSocketLayer();
+ d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);
+#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) {
+ d->socketError = UnsupportedSocketOperationError;
+ setErrorString(tr("Operation on socket is not supported"));
+ return false;
+ }
+ bool result = d->socketEngine->initialize(socketDescriptor, socketState);
+ if (!result) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+ return false;
+ }
+
+ if (d->threadData->eventDispatcher)
+ d->socketEngine->setReceiver(d);
+
+ QIODevice::open(openMode);
+
+ if (d->state != socketState) {
+ d->state = socketState;
+ emit stateChanged(d->state);
+ }
+
+ d->pendingClose = false;
+ d->socketEngine->setReadNotificationEnabled(true);
+ d->localPort = d->socketEngine->localPort();
+ d->peerPort = d->socketEngine->peerPort();
+ d->localAddress = d->socketEngine->localAddress();
+ d->peerAddress = d->socketEngine->peerAddress();
+ d->cachedSocketDescriptor = socketDescriptor;
+
+ return true;
+}
+
+/*!
+ \since 4.6
+ Sets the given \a option to the value described by \a value.
+
+ \sa socketOption()
+*/
+void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
+{
+#ifndef QT_NO_OPENSSL
+ if (QSslSocket *sslSocket = qobject_cast<QSslSocket*>(this)) {
+ sslSocket->setSocketOption(option, value);
+ return;
+ }
+#endif
+
+ if (!d_func()->socketEngine)
+ return;
+
+ switch (option) {
+ case LowDelayOption:
+ d_func()->socketEngine->setOption(QAbstractSocketEngine::LowDelayOption, value.toInt());
+ break;
+
+ 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;
+ }
+}
+
+/*!
+ \since 4.6
+ Returns the value of the \a option option.
+
+ \sa setSocketOption()
+*/
+QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option)
+{
+#ifndef QT_NO_OPENSSL
+ if (QSslSocket *sslSocket = qobject_cast<QSslSocket*>(this)) {
+ return sslSocket->socketOption(option);
+ }
+#endif
+
+ if (!d_func()->socketEngine)
+ return QVariant();
+
+ int ret = -1;
+ switch (option) {
+ case LowDelayOption:
+ ret = d_func()->socketEngine->option(QAbstractSocketEngine::LowDelayOption);
+ break;
+
+ 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();
+ else
+ return QVariant(ret);
+}
+
+
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+/*!
+ Waits until the socket is connected, up to \a msecs
+ milliseconds. If the connection has been established, this
+ function returns true; otherwise it returns false. In the case
+ where it returns false, you can call error() to determine
+ the cause of the error.
+
+ The following example waits up to one second for a connection
+ to be established:
+
+ \snippet doc/src/snippets/code/src_network_socket_qabstractsocket.cpp 0
+
+ If msecs is -1, this function will not time out.
+
+ \note This function may wait slightly longer than \a msecs,
+ depending on the time it takes to complete the host lookup.
+
+ \note Multiple calls to this functions do not accumulate the time.
+ If the function times out, the connecting process will be aborted.
+
+ \sa connectToHost(), connected()
+*/
+bool QAbstractSocket::waitForConnected(int msecs)
+{
+ Q_D(QAbstractSocket);
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForConnected(%i)", msecs);
+#endif
+
+ if (state() == ConnectedState) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForConnected(%i) already connected", msecs);
+#endif
+ return true;
+ }
+
+#ifndef QT_NO_OPENSSL
+ // Manual polymorphism; this function is not virtual, but has an overload
+ // in QSslSocket.
+ if (QSslSocket *socket = qobject_cast<QSslSocket *>(this))
+ return socket->waitForConnected(msecs);
+#endif
+
+ bool wasPendingClose = d->pendingClose;
+ d->pendingClose = false;
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ if (d->state == HostLookupState) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForConnected(%i) doing host name lookup", 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)
+ return false; // connect not im progress anymore!
+
+ bool timedOut = true;
+#if defined (QABSTRACTSOCKET_DEBUG)
+ int attempt = 1;
+#endif
+ while (state() == ConnectingState && (msecs == -1 || stopWatch.elapsed() < msecs)) {
+ int timeout = qt_timeout_value(msecs, stopWatch.elapsed());
+ if (msecs != -1 && timeout > QT_CONNECT_TIMEOUT)
+ timeout = QT_CONNECT_TIMEOUT;
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForConnected(%i) waiting %.2f secs for connection attempt #%i",
+ msecs, timeout / 1000.0, attempt++);
+#endif
+ timedOut = false;
+
+ if (d->socketEngine && d->socketEngine->waitForWrite(timeout, &timedOut) && !timedOut) {
+ d->_q_testConnection();
+ } else {
+ d->_q_connectToNextAddress();
+ }
+ }
+
+ if ((timedOut && state() != ConnectedState) || state() == ConnectingState) {
+ d->socketError = SocketTimeoutError;
+ d->state = UnconnectedState;
+ emit stateChanged(d->state);
+ d->resetSocketLayer();
+ setErrorString(tr("Socket operation timed out"));
+ }
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForConnected(%i) == %s", msecs,
+ state() == ConnectedState ? "true" : "false");
+#endif
+ if (state() != ConnectedState)
+ return false;
+ if (wasPendingClose)
+ disconnectFromHost();
+ return true;
+}
+
+/*!
+ This function blocks until new data is available for reading and the
+ \l{QIODevice::}{readyRead()} signal has been emitted. The function
+ will timeout after \a msecs milliseconds; the default timeout is
+ 30000 milliseconds.
+
+ The function returns true if the readyRead() signal is emitted and
+ there is new data available for reading; otherwise it returns false
+ (if an error occurred or the operation timed out).
+
+ \sa waitForBytesWritten()
+*/
+bool QAbstractSocket::waitForReadyRead(int msecs)
+{
+ Q_D(QAbstractSocket);
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForReadyRead(%i)", msecs);
+#endif
+
+ // require calling connectToHost() before waitForReadyRead()
+ if (state() == UnconnectedState) {
+ /* If all you have is a QIODevice pointer to an abstractsocket, you cannot check
+ this, so you cannot avoid this warning. */
+// qWarning("QAbstractSocket::waitForReadyRead() is not allowed in UnconnectedState");
+ return false;
+ }
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // handle a socket in connecting state
+ if (state() == HostLookupState || state() == ConnectingState) {
+ if (!waitForConnected(msecs))
+ return false;
+ }
+
+ Q_ASSERT(d->socketEngine);
+ forever {
+ bool readyToRead = false;
+ bool readyToWrite = false;
+ if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
+ qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
+ msecs, d->socketError, errorString().toLatin1().constData());
+#endif
+ emit error(d->socketError);
+ if (d->socketError != SocketTimeoutError)
+ close();
+ return false;
+ }
+
+ if (readyToRead) {
+ if (d->canReadNotification())
+ return true;
+ }
+
+ if (readyToWrite)
+ d->canWriteNotification();
+
+ if (state() != ConnectedState)
+ return false;
+ }
+ return false;
+}
+
+/*! \reimp
+ */
+bool QAbstractSocket::waitForBytesWritten(int msecs)
+{
+ Q_D(QAbstractSocket);
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForBytesWritten(%i)", msecs);
+#endif
+
+ // require calling connectToHost() before waitForBytesWritten()
+ if (state() == UnconnectedState) {
+ qWarning("QAbstractSocket::waitForBytesWritten() is not allowed in UnconnectedState");
+ return false;
+ }
+
+ if (d->writeBuffer.isEmpty())
+ return false;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // handle a socket in connecting state
+ if (state() == HostLookupState || state() == ConnectingState) {
+ if (!waitForConnected(msecs))
+ return false;
+ }
+
+ forever {
+ bool readyToRead = false;
+ bool readyToWrite = false;
+ if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
+ qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%i, %s)",
+ msecs, d->socketError, errorString().toLatin1().constData());
+#endif
+ emit error(d->socketError);
+ if (d->socketError != SocketTimeoutError)
+ close();
+ return false;
+ }
+
+ if (readyToRead) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForBytesWritten calls canReadNotification");
+#endif
+ if(!d->canReadNotification())
+ return false;
+ }
+
+
+ if (readyToWrite) {
+ if (d->canWriteNotification()) {
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForBytesWritten returns true");
+#endif
+ return true;
+ }
+ }
+
+ if (state() != ConnectedState)
+ return false;
+ }
+ return false;
+}
+
+/*!
+ Waits until the socket has disconnected, up to \a msecs
+ milliseconds. If the connection has been disconnected, this
+ function returns true; otherwise it returns false. In the case
+ where it returns false, you can call error() to determine
+ the cause of the error.
+
+ The following example waits up to one second for a connection
+ to be closed:
+
+ \snippet doc/src/snippets/code/src_network_socket_qabstractsocket.cpp 1
+
+ If msecs is -1, this function will not time out.
+
+ \sa disconnectFromHost(), close()
+*/
+bool QAbstractSocket::waitForDisconnected(int msecs)
+{
+ Q_D(QAbstractSocket);
+#ifndef QT_NO_OPENSSL
+ // Manual polymorphism; this function is not virtual, but has an overload
+ // in QSslSocket.
+ if (QSslSocket *socket = qobject_cast<QSslSocket *>(this))
+ return socket->waitForDisconnected(msecs);
+#endif
+
+ // require calling connectToHost() before waitForDisconnected()
+ if (state() == UnconnectedState) {
+ qWarning("QAbstractSocket::waitForDisconnected() is not allowed in UnconnectedState");
+ return false;
+ }
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // handle a socket in connecting state
+ if (state() == HostLookupState || state() == ConnectingState) {
+ if (!waitForConnected(msecs))
+ return false;
+ if (state() == UnconnectedState)
+ return true;
+ }
+
+ forever {
+ bool readyToRead = false;
+ bool readyToWrite = false;
+ if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, state() == ConnectedState,
+ !d->writeBuffer.isEmpty(),
+ qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
+ msecs, d->socketError, errorString().toLatin1().constData());
+#endif
+ emit error(d->socketError);
+ if (d->socketError != SocketTimeoutError)
+ close();
+ return false;
+ }
+
+ if (readyToRead)
+ d->canReadNotification();
+ if (readyToWrite)
+ d->canWriteNotification();
+
+ if (state() == UnconnectedState)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Aborts the current connection and resets the socket. Unlike disconnectFromHost(),
+ this function immediately closes the socket, discarding any pending data in the
+ write buffer.
+
+ \sa disconnectFromHost(), close()
+*/
+void QAbstractSocket::abort()
+{
+ Q_D(QAbstractSocket);
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::abort()");
+#endif
+ if (d->state == UnconnectedState)
+ return;
+#ifndef QT_NO_OPENSSL
+ if (QSslSocket *socket = qobject_cast<QSslSocket *>(this)) {
+ socket->abort();
+ return;
+ }
+#endif
+ if (d->connectTimer) {
+ d->connectTimer->stop();
+ delete d->connectTimer;
+ d->connectTimer = 0;
+ }
+
+ d->writeBuffer.clear();
+ d->abortCalled = true;
+ close();
+}
+
+/*! \reimp
+*/
+bool QAbstractSocket::isSequential() const
+{
+ return true;
+}
+
+/*! \reimp
+
+ Returns true if no more data is currently
+ available for reading; otherwise returns false.
+
+ This function is most commonly used when reading data from the
+ socket in a loop. For example:
+
+ \snippet doc/src/snippets/code/src_network_socket_qabstractsocket.cpp 2
+
+ \sa bytesAvailable(), readyRead()
+ */
+bool QAbstractSocket::atEnd() const
+{
+ return QIODevice::atEnd() && (!isOpen() || d_func()->readBuffer.isEmpty());
+}
+
+/*!
+ This function writes as much as possible from the internal write buffer to
+ the underlying network socket, without blocking. If any data was written,
+ this function returns true; otherwise false is returned.
+
+ Call this function if you need QAbstractSocket to start sending buffered
+ data immediately. The number of bytes successfully written depends on the
+ operating system. In most cases, you do not need to call this function,
+ because QAbstractSocket will start sending data automatically once control
+ goes back to the event loop. In the absence of an event loop, call
+ waitForBytesWritten() instead.
+
+ \sa write(), waitForBytesWritten()
+*/
+// Note! docs copied to QSslSocket::flush()
+bool QAbstractSocket::flush()
+{
+ Q_D(QAbstractSocket);
+#ifndef QT_NO_OPENSSL
+ // Manual polymorphism; flush() isn't virtual, but QSslSocket overloads
+ // it.
+ if (QSslSocket *socket = qobject_cast<QSslSocket *>(this))
+ return socket->flush();
+#endif
+ Q_CHECK_SOCKETENGINE(false);
+ return d->flush();
+}
+
+/*! \reimp
+*/
+qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
+{
+ Q_D(QAbstractSocket);
+
+ // 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);
+
+ // 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 [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) {
+ const char *ptr = d->readBuffer.readPointer();
+ int bytesToReadFromThisBlock = qMin(int(bytesToRead - readSoFar),
+ d->readBuffer.nextDataBlockSize());
+ memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
+ readSoFar += bytesToReadFromThisBlock;
+ 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 [unreachable]",
+ data, qt_prettyDebug(data, qMin<qint64>(32, readSoFar), readSoFar).data(),
+ maxSize, readSoFar);
+#endif
+ return readSoFar;
+}
+
+/*! \reimp
+*/
+qint64 QAbstractSocket::readLineData(char *data, qint64 maxlen)
+{
+ return QIODevice::readLineData(data, maxlen);
+}
+
+/*! \reimp
+*/
+qint64 QAbstractSocket::writeData(const char *data, qint64 size)
+{
+ Q_D(QAbstractSocket);
+ if (d->state == QAbstractSocket::UnconnectedState) {
+ d->socketError = QAbstractSocket::UnknownSocketError;
+ setErrorString(tr("Socket is not connected"));
+ return -1;
+ }
+
+ 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();
+ setErrorString(d->socketEngine->errorString());
+ } else if (!d->writeBuffer.isEmpty()) {
+ d->socketEngine->setWriteNotificationEnabled(true);
+ }
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
+ qt_prettyDebug(data, qMin((int)size, 32), size).data(),
+ size, written);
+#endif
+ if (written >= 0)
+ emit bytesWritten(written);
+ 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;
+ else
+ memcpy(ptr, data, size);
+
+ qint64 written = size;
+
+ if (d->socketEngine && !d->writeBuffer.isEmpty())
+ d->socketEngine->setWriteNotificationEnabled(true);
+
+#if defined (QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::writeData(%p \"%s\", %lli) == %lli", data,
+ qt_prettyDebug(data, qMin((int)size, 32), size).data(),
+ size, written);
+#endif
+ return written;
+}
+
+/*!
+ \since 4.1
+
+ Sets the port on the local side of a connection to \a port.
+
+ You can call this function in a subclass of QAbstractSocket to
+ change the return value of the localPort() function after a
+ connection has been established. This feature is commonly used by
+ proxy connections for virtual connection settings.
+
+ Note that this function does not bind the local port of the socket
+ prior to a connection (e.g., QUdpSocket::bind()).
+
+ \sa localAddress(), setLocalAddress(), setPeerPort()
+*/
+void QAbstractSocket::setLocalPort(quint16 port)
+{
+ Q_D(QAbstractSocket);
+ d->localPort = port;
+}
+
+/*!
+ \since 4.1
+
+ Sets the address on the local side of a connection to
+ \a address.
+
+ You can call this function in a subclass of QAbstractSocket to
+ change the return value of the localAddress() function after a
+ connection has been established. This feature is commonly used by
+ proxy connections for virtual connection settings.
+
+ Note that this function does not bind the local address of the socket
+ prior to a connection (e.g., QUdpSocket::bind()).
+
+ \sa localAddress(), setLocalPort(), setPeerAddress()
+*/
+void QAbstractSocket::setLocalAddress(const QHostAddress &address)
+{
+ Q_D(QAbstractSocket);
+ d->localAddress = address;
+}
+
+/*!
+ \since 4.1
+
+ Sets the port of the remote side of the connection to
+ \a port.
+
+ You can call this function in a subclass of QAbstractSocket to
+ change the return value of the peerPort() function after a
+ connection has been established. This feature is commonly used by
+ proxy connections for virtual connection settings.
+
+ \sa peerPort(), setPeerAddress(), setLocalPort()
+*/
+void QAbstractSocket::setPeerPort(quint16 port)
+{
+ Q_D(QAbstractSocket);
+ d->peerPort = port;
+}
+
+/*!
+ \since 4.1
+
+ Sets the address of the remote side of the connection
+ to \a address.
+
+ You can call this function in a subclass of QAbstractSocket to
+ change the return value of the peerAddress() function after a
+ connection has been established. This feature is commonly used by
+ proxy connections for virtual connection settings.
+
+ \sa peerAddress(), setPeerPort(), setLocalAddress()
+*/
+void QAbstractSocket::setPeerAddress(const QHostAddress &address)
+{
+ Q_D(QAbstractSocket);
+ d->peerAddress = address;
+}
+
+/*!
+ \since 4.1
+
+ Sets the host name of the remote peer to \a name.
+
+ You can call this function in a subclass of QAbstractSocket to
+ change the return value of the peerName() function after a
+ connection has been established. This feature is commonly used by
+ proxy connections for virtual connection settings.
+
+ \sa peerName()
+*/
+void QAbstractSocket::setPeerName(const QString &name)
+{
+ Q_D(QAbstractSocket);
+ d->peerName = name;
+}
+
+/*!
+ Closes the I/O device for the socket, disconnects the socket's connection with the
+ host, closes the socket, and resets the name, address, port number and underlying
+ socket descriptor.
+
+ See QIODevice::close() for a description of the actions that occur when an I/O
+ device is closed.
+
+ \sa abort()
+*/
+void QAbstractSocket::close()
+{
+ Q_D(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::close()");
+#endif
+ QIODevice::close();
+ if (d->state != UnconnectedState) {
+ d->closeCalled = true;
+ disconnectFromHost();
+ }
+
+ d->localPort = 0;
+ d->peerPort = 0;
+ d->localAddress.clear();
+ d->peerAddress.clear();
+ d->peerName.clear();
+ d->cachedSocketDescriptor = -1;
+}
+
+/*!
+ Attempts to close the socket. If there is pending data waiting to
+ be written, QAbstractSocket will enter ClosingState and wait
+ until all data has been written. Eventually, it will enter
+ UnconnectedState and emit the disconnected() signal.
+
+ \sa connectToHost()
+*/
+void QAbstractSocket::disconnectFromHost()
+{
+ QMetaObject::invokeMethod(this, "disconnectFromHostImplementation",
+ Qt::DirectConnection);
+}
+
+/*!
+ \since 4.1
+
+ Contains the implementation of disconnectFromHost().
+*/
+void QAbstractSocket::disconnectFromHostImplementation()
+{
+ Q_D(QAbstractSocket);
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost()");
+#endif
+
+ if (d->state == UnconnectedState) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() was called on an unconnected socket");
+#endif
+ return;
+ }
+
+ if (!d->abortCalled && (d->state == ConnectingState || d->state == HostLookupState)) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() but we're still connecting");
+#endif
+ d->pendingClose = true;
+ return;
+ }
+
+#ifdef QT3_SUPPORT
+ emit connectionClosed(); // compat signal
+#endif
+
+ // Disable and delete read notification
+ if (d->socketEngine)
+ d->socketEngine->setReadNotificationEnabled(false);
+
+ if (d->abortCalled) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() aborting immediately");
+#endif
+ if (d->state == HostLookupState) {
+ QHostInfo::abortHostLookup(d->hostLookupId);
+ d->hostLookupId = -1;
+ }
+ } else {
+ // Perhaps emit closing()
+ if (d->state != ClosingState) {
+ d->state = ClosingState;
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() emits stateChanged()(ClosingState)");
+#endif
+ emit stateChanged(d->state);
+ } else {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() return from delayed close");
+#endif
+ }
+
+ // Wait for pending data to be written.
+ if (d->socketEngine && d->socketEngine->isValid() && (d->writeBuffer.size() > 0
+ || d->socketEngine->bytesToWrite() > 0)) {
+ // hack: when we are waiting for the socket engine to write bytes (only
+ // possible when using Socks5 or HTTP socket engine), then close
+ // anyway after 2 seconds. This is to prevent a timeout on Mac, where we
+ // sometimes just did not get the write notifier from the underlying
+ // CFSocket and no progress was made.
+ if (d->writeBuffer.size() == 0 && d->socketEngine->bytesToWrite() > 0) {
+ if (!d->disconnectTimer) {
+ d->disconnectTimer = new QTimer(this);
+ connect(d->disconnectTimer, SIGNAL(timeout()), this,
+ SLOT(_q_forceDisconnect()), Qt::DirectConnection);
+ }
+ if (!d->disconnectTimer->isActive())
+ d->disconnectTimer->start(2000);
+ }
+ d->socketEngine->setWriteNotificationEnabled(true);
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() delaying disconnect");
+#endif
+ return;
+ } else {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() disconnecting immediately");
+#endif
+ }
+ }
+
+ SocketState previousState = d->state;
+ d->resetSocketLayer();
+ d->state = UnconnectedState;
+ emit stateChanged(d->state);
+ emit readChannelFinished(); // we got an EOF
+
+#ifdef QT3_SUPPORT
+ emit delayedCloseFinished(); // compat signal
+#endif
+ // only emit disconnected if we were connected before
+ if (previousState == ConnectedState || previousState == ClosingState)
+ emit disconnected();
+
+ d->localPort = 0;
+ d->peerPort = 0;
+ d->localAddress.clear();
+ d->peerAddress.clear();
+
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() disconnected!");
+#endif
+
+ if (d->closeCalled) {
+#if defined(QABSTRACTSOCKET_DEBUG)
+ qDebug("QAbstractSocket::disconnectFromHost() closed!");
+#endif
+ d->readBuffer.clear();
+ d->writeBuffer.clear();
+ QIODevice::close();
+ }
+}
+
+/*!
+ Returns the size of the internal read buffer. This limits the
+ amount of data that the client can receive before you call read()
+ or readAll().
+
+ A read buffer size of 0 (the default) means that the buffer has
+ no size limit, ensuring that no data is lost.
+
+ \sa setReadBufferSize(), read()
+*/
+qint64 QAbstractSocket::readBufferSize() const
+{
+ return d_func()->readBufferMaxSize;
+}
+
+/*!
+ Sets the size of QAbstractSocket's internal read buffer to be \a
+ size bytes.
+
+ If the buffer size is limited to a certain size, QAbstractSocket
+ won't buffer more than this size of data. Exceptionally, a buffer
+ size of 0 means that the read buffer is unlimited and all
+ incoming data is buffered. This is the default.
+
+ This option is useful if you only read the data at certain points
+ in time (e.g., in a real-time streaming application) or if you
+ want to protect your socket against receiving too much data,
+ which may eventually cause your application to run out of memory.
+
+ Only QTcpSocket uses QAbstractSocket's internal buffer; QUdpSocket
+ does not use any buffering at all, but rather relies on the
+ implicit buffering provided by the operating system.
+ Because of this, calling this function on QUdpSocket has no
+ effect.
+
+ \sa readBufferSize(), read()
+*/
+void QAbstractSocket::setReadBufferSize(qint64 size)
+{
+ Q_D(QAbstractSocket);
+
+#ifndef QT_NO_OPENSSL
+ // Manual polymorphism; setReadBufferSize() isn't virtual, but QSslSocket overloads
+ // it.
+ if (QSslSocket *socket = qobject_cast<QSslSocket *>(this)) {
+ socket->setReadBufferSize(size);
+ return;
+ }
+#endif
+
+ if (d->readBufferMaxSize == size)
+ return;
+ d->readBufferMaxSize = size;
+ if (!d->readSocketNotifierCalled && d->socketEngine) {
+ // 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) && d->state == QAbstractSocket::ConnectedState) // Do not change the notifier unless we are connected.
+ d->socketEngine->setReadNotificationEnabled(true);
+ }
+}
+
+/*!
+ Returns the state of the socket.
+
+ \sa error()
+*/
+QAbstractSocket::SocketState QAbstractSocket::state() const
+{
+ return d_func()->state;
+}
+
+/*!
+ Sets the state of the socket to \a state.
+
+ \sa state()
+*/
+void QAbstractSocket::setSocketState(SocketState state)
+{
+ d_func()->state = state;
+}
+
+/*!
+ Returns the socket type (TCP, UDP, or other).
+
+ \sa QTcpSocket, QUdpSocket
+*/
+QAbstractSocket::SocketType QAbstractSocket::socketType() const
+{
+ return d_func()->socketType;
+}
+
+/*!
+ Returns the type of error that last occurred.
+
+ \sa state(), errorString()
+*/
+QAbstractSocket::SocketError QAbstractSocket::error() const
+{
+ return d_func()->socketError;
+}
+
+/*!
+ Sets the type of error that last occurred to \a socketError.
+
+ \sa setSocketState(), setErrorString()
+*/
+void QAbstractSocket::setSocketError(SocketError socketError)
+{
+ d_func()->socketError = socketError;
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*!
+ \since 4.1
+
+ Sets the explicit network proxy for this socket to \a networkProxy.
+
+ To disable the use of a proxy for this socket, use the
+ QNetworkProxy::NoProxy proxy type:
+
+ \snippet doc/src/snippets/code/src_network_socket_qabstractsocket.cpp 3
+
+ The default value for the proxy is QNetworkProxy::DefaultProxy,
+ which means the socket will use the application settings: if a
+ proxy is set with QNetworkProxy::setApplicationProxy, it will use
+ that; otherwise, if a factory is set with
+ QNetworkProxyFactory::setApplicationProxyFactory, it will query
+ that factory with type QNetworkProxyQuery::TcpSocket.
+
+ \sa proxy(), QNetworkProxy, QNetworkProxyFactory::queryProxy()
+*/
+void QAbstractSocket::setProxy(const QNetworkProxy &networkProxy)
+{
+ Q_D(QAbstractSocket);
+ d->proxy = networkProxy;
+}
+
+/*!
+ \since 4.1
+
+ Returns the network proxy for this socket.
+ By default QNetworkProxy::DefaultProxy is used, which means this
+ socket will query the default proxy settings for the application.
+
+ \sa setProxy(), QNetworkProxy, QNetworkProxyFactory
+*/
+QNetworkProxy QAbstractSocket::proxy() const
+{
+ Q_D(const QAbstractSocket);
+ return d->proxy;
+}
+#endif // QT_NO_NETWORKPROXY
+
+#ifdef QT3_SUPPORT
+/*!
+ \enum QAbstractSocket::Error
+ \compat
+
+ Use QAbstractSocket::SocketError instead.
+
+ \value ErrConnectionRefused Use QAbstractSocket::ConnectionRefusedError instead.
+ \value ErrHostNotFound Use QAbstractSocket::HostNotFoundError instead.
+ \value ErrSocketRead Use QAbstractSocket::UnknownSocketError instead.
+*/
+
+/*!
+ \typedef QAbstractSocket::State
+ \compat
+
+ Use QAbstractSocket::SocketState instead.
+
+ \table
+ \header \o Qt 3 enum value \o Qt 4 enum value
+ \row \o \c Idle \o \l UnconnectedState
+ \row \o \c HostLookup \o \l HostLookupState
+ \row \o \c Connecting \o \l ConnectingState
+ \row \o \c Connected \o \l ConnectedState
+ \row \o \c Closing \o \l ClosingState
+ \row \o \c Connection \o \l ConnectedState
+ \endtable
+*/
+
+/*!
+ \fn int QAbstractSocket::socket() const
+
+ Use socketDescriptor() instead.
+*/
+
+/*!
+ \fn void QAbstractSocket::setSocket(int socket)
+
+ Use setSocketDescriptor() instead.
+*/
+
+/*!
+ \fn Q_ULONG QAbstractSocket::waitForMore(int msecs, bool *timeout = 0) const
+
+ Use waitForReadyRead() instead.
+
+ \oldcode
+ bool timeout;
+ Q_ULONG numBytes = socket->waitForMore(30000, &timeout);
+ \newcode
+ qint64 numBytes = 0;
+ if (socket->waitForReadyRead(msecs))
+ numBytes = socket->bytesAvailable();
+ bool timeout = (error() == QAbstractSocket::SocketTimeoutError);
+ \endcode
+
+ \sa waitForReadyRead(), bytesAvailable(), error(), SocketTimeoutError
+*/
+
+/*!
+ \fn void QAbstractSocket::connectionClosed()
+
+ Use disconnected() instead.
+*/
+
+/*!
+ \fn void QAbstractSocket::delayedCloseFinished()
+
+ Use disconnected() instead.
+*/
+#endif // QT3_SUPPORT
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QAbstractSocket::SocketError error)
+{
+ switch (error) {
+ case QAbstractSocket::ConnectionRefusedError:
+ debug << "QAbstractSocket::ConnectionRefusedError";
+ break;
+ case QAbstractSocket::RemoteHostClosedError:
+ debug << "QAbstractSocket::RemoteHostClosedError";
+ break;
+ case QAbstractSocket::HostNotFoundError:
+ debug << "QAbstractSocket::HostNotFoundError";
+ break;
+ case QAbstractSocket::SocketAccessError:
+ debug << "QAbstractSocket::SocketAccessError";
+ break;
+ case QAbstractSocket::SocketResourceError:
+ debug << "QAbstractSocket::SocketResourceError";
+ break;
+ case QAbstractSocket::SocketTimeoutError:
+ debug << "QAbstractSocket::SocketTimeoutError";
+ break;
+ case QAbstractSocket::DatagramTooLargeError:
+ debug << "QAbstractSocket::DatagramTooLargeError";
+ break;
+ case QAbstractSocket::NetworkError:
+ debug << "QAbstractSocket::NetworkError";
+ break;
+ case QAbstractSocket::AddressInUseError:
+ debug << "QAbstractSocket::AddressInUseError";
+ break;
+ case QAbstractSocket::SocketAddressNotAvailableError:
+ debug << "QAbstractSocket::SocketAddressNotAvailableError";
+ break;
+ case QAbstractSocket::UnsupportedSocketOperationError:
+ debug << "QAbstractSocket::UnsupportedSocketOperationError";
+ break;
+ case QAbstractSocket::UnfinishedSocketOperationError:
+ debug << "QAbstractSocket::UnfinishedSocketOperationError";
+ break;
+ case QAbstractSocket::ProxyAuthenticationRequiredError:
+ debug << "QAbstractSocket::ProxyAuthenticationRequiredError";
+ break;
+ case QAbstractSocket::UnknownSocketError:
+ debug << "QAbstractSocket::UnknownSocketError";
+ break;
+ case QAbstractSocket::ProxyConnectionRefusedError:
+ debug << "QAbstractSocket::ProxyConnectionRefusedError";
+ break;
+ case QAbstractSocket::ProxyConnectionClosedError:
+ debug << "QAbstractSocket::ProxyConnectionClosedError";
+ break;
+ case QAbstractSocket::ProxyConnectionTimeoutError:
+ debug << "QAbstractSocket::ProxyConnectionTimeoutError";
+ break;
+ case QAbstractSocket::ProxyNotFoundError:
+ debug << "QAbstractSocket::ProxyNotFoundError";
+ break;
+ case QAbstractSocket::ProxyProtocolError:
+ debug << "QAbstractSocket::ProxyProtocolError";
+ break;
+ default:
+ debug << "QAbstractSocket::SocketError(" << int(error) << ')';
+ break;
+ }
+ return debug;
+}
+
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QAbstractSocket::SocketState state)
+{
+ switch (state) {
+ case QAbstractSocket::UnconnectedState:
+ debug << "QAbstractSocket::UnconnectedState";
+ break;
+ case QAbstractSocket::HostLookupState:
+ debug << "QAbstractSocket::HostLookupState";
+ break;
+ case QAbstractSocket::ConnectingState:
+ debug << "QAbstractSocket::ConnectingState";
+ break;
+ case QAbstractSocket::ConnectedState:
+ debug << "QAbstractSocket::ConnectedState";
+ break;
+ case QAbstractSocket::BoundState:
+ debug << "QAbstractSocket::BoundState";
+ break;
+ case QAbstractSocket::ListeningState:
+ debug << "QAbstractSocket::ListeningState";
+ break;
+ case QAbstractSocket::ClosingState:
+ debug << "QAbstractSocket::ClosingState";
+ break;
+ default:
+ debug << "QAbstractSocket::SocketState(" << int(state) << ')';
+ break;
+ }
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qabstractsocket.cpp"
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
new file mode 100644
index 0000000000..24f5478911
--- /dev/null
+++ b/src/network/socket/qabstractsocket.h
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSOCKET_H
+#define QABSTRACTSOCKET_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qobject.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QHostAddress;
+#ifndef QT_NO_NETWORKPROXY
+class QNetworkProxy;
+#endif
+class QAbstractSocketPrivate;
+class QAuthenticator;
+
+class Q_NETWORK_EXPORT QAbstractSocket : public QIODevice
+{
+ Q_OBJECT
+ Q_ENUMS(SocketType NetworkLayerProtocol SocketError SocketState SocketOption)
+public:
+ enum SocketType {
+ TcpSocket,
+ UdpSocket,
+ UnknownSocketType = -1
+ };
+ enum NetworkLayerProtocol {
+ IPv4Protocol,
+ IPv6Protocol,
+ UnknownNetworkLayerProtocol = -1
+ };
+ enum SocketError {
+ ConnectionRefusedError,
+ RemoteHostClosedError,
+ HostNotFoundError,
+ SocketAccessError,
+ SocketResourceError,
+ SocketTimeoutError, /* 5 */
+ DatagramTooLargeError,
+ NetworkError,
+ AddressInUseError,
+ SocketAddressNotAvailableError,
+ UnsupportedSocketOperationError, /* 10 */
+ UnfinishedSocketOperationError,
+ ProxyAuthenticationRequiredError,
+ SslHandshakeFailedError,
+ ProxyConnectionRefusedError,
+ ProxyConnectionClosedError, /* 15 */
+ ProxyConnectionTimeoutError,
+ ProxyNotFoundError,
+ ProxyProtocolError,
+
+ UnknownSocketError = -1
+ };
+ enum SocketState {
+ UnconnectedState,
+ HostLookupState,
+ ConnectingState,
+ ConnectedState,
+ BoundState,
+ ListeningState,
+ ClosingState
+#ifdef QT3_SUPPORT
+ ,
+ Idle = UnconnectedState,
+ HostLookup = HostLookupState,
+ Connecting = ConnectingState,
+ Connected = ConnectedState,
+ Closing = ClosingState,
+ Connection = ConnectedState
+#endif
+ };
+ enum SocketOption {
+ LowDelayOption, // TCP_NODELAY
+ KeepAliveOption, // SO_KEEPALIVE
+ MulticastTtlOption, // IP_MULTICAST_TTL
+ MulticastLoopbackOption // IP_MULTICAST_LOOPBACK
+ };
+
+ QAbstractSocket(SocketType socketType, QObject *parent);
+ virtual ~QAbstractSocket();
+
+ // ### Qt 5: Make connectToHost() and disconnectFromHost() virtual.
+ void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite);
+ void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
+ void disconnectFromHost();
+
+ bool isValid() const;
+
+ qint64 bytesAvailable() const;
+ qint64 bytesToWrite() const;
+
+ bool canReadLine() const;
+
+ quint16 localPort() const;
+ QHostAddress localAddress() const;
+ quint16 peerPort() const;
+ QHostAddress peerAddress() const;
+ QString peerName() const;
+
+ // ### Qt 5: Make setReadBufferSize() virtual
+ qint64 readBufferSize() const;
+ void setReadBufferSize(qint64 size);
+
+ void abort();
+
+ // ### Qt 5: Make socketDescriptor() and setSocketDescriptor() virtual.
+ int socketDescriptor() const;
+ bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState,
+ OpenMode openMode = ReadWrite);
+
+ // ### Qt 5: Make virtual?
+ void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value);
+ QVariant socketOption(QAbstractSocket::SocketOption option);
+
+ SocketType socketType() const;
+ SocketState state() const;
+ SocketError error() const;
+
+ // from QIODevice
+ void close();
+ bool isSequential() const;
+ bool atEnd() const;
+ bool flush();
+
+ // for synchronous access
+ // ### Qt 5: Make waitForConnected() and waitForDisconnected() virtual.
+ bool waitForConnected(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000);
+ bool waitForBytesWritten(int msecs = 30000);
+ bool waitForDisconnected(int msecs = 30000);
+
+#ifndef QT_NO_NETWORKPROXY
+ void setProxy(const QNetworkProxy &networkProxy);
+ QNetworkProxy proxy() const;
+#endif
+
+Q_SIGNALS:
+ void hostFound();
+ void connected();
+ void disconnected();
+ void stateChanged(QAbstractSocket::SocketState);
+ void error(QAbstractSocket::SocketError);
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
+#endif
+
+protected Q_SLOTS:
+ void connectToHostImplementation(const QString &hostName, quint16 port, OpenMode mode = ReadWrite);
+ void disconnectFromHostImplementation();
+
+protected:
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 readLineData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+ void setSocketState(SocketState state);
+ void setSocketError(SocketError socketError);
+ void setLocalPort(quint16 port);
+ void setLocalAddress(const QHostAddress &address);
+ void setPeerPort(quint16 port);
+ void setPeerAddress(const QHostAddress &address);
+ void setPeerName(const QString &name);
+
+ QAbstractSocket(SocketType socketType, QAbstractSocketPrivate &dd, QObject *parent = 0);
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractSocket)
+ Q_DISABLE_COPY(QAbstractSocket)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_connectToNextAddress())
+ Q_PRIVATE_SLOT(d_func(), void _q_startConnecting(const QHostInfo &))
+ Q_PRIVATE_SLOT(d_func(), void _q_abortConnectionAttempt())
+ Q_PRIVATE_SLOT(d_func(), void _q_testConnection())
+ Q_PRIVATE_SLOT(d_func(), void _q_forceDisconnect())
+
+#ifdef QT3_SUPPORT
+public:
+ enum Error {
+ ErrConnectionRefused = ConnectionRefusedError,
+ ErrHostNotFound = HostNotFoundError,
+ ErrSocketRead = UnknownSocketError
+ };
+ inline QT3_SUPPORT int socket() const { return socketDescriptor(); }
+ inline QT3_SUPPORT void setSocket(int socket) { setSocketDescriptor(socket); }
+ inline QT3_SUPPORT qulonglong waitForMore(int msecs, bool *timeout = 0) const
+ {
+ QAbstractSocket *that = const_cast<QAbstractSocket *>(this);
+ if (that->waitForReadyRead(msecs))
+ return qulonglong(bytesAvailable());
+ if (error() == SocketTimeoutError && timeout)
+ *timeout = true;
+ return 0;
+ }
+ typedef SocketState State;
+Q_SIGNALS:
+ QT_MOC_COMPAT void connectionClosed(); // same as disconnected()
+ QT_MOC_COMPAT void delayedCloseFinished(); // same as disconnected()
+
+
+#endif
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_NETWORK_EXPORT QDebug operator<<(QDebug, QAbstractSocket::SocketError);
+Q_NETWORK_EXPORT QDebug operator<<(QDebug, QAbstractSocket::SocketState);
+#endif
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTSOCKET_H
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
new file mode 100644
index 0000000000..7662f47fbf
--- /dev/null
+++ b/src/network/socket/qabstractsocket_p.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSOCKET_P_H
+#define QABSTRACTSOCKET_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 QAbstractSocket class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtNetwork/qabstractsocket.h"
+#include "QtCore/qbytearray.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qtimer.h"
+#include "private/qringbuffer_p.h"
+#include "private/qiodevice_p.h"
+#include "private/qabstractsocketengine_p.h"
+#include "qnetworkproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+class QHostInfo;
+
+class QAbstractSocketPrivate : public QIODevicePrivate, public QAbstractSocketEngineReceiver
+{
+ Q_DECLARE_PUBLIC(QAbstractSocket)
+public:
+ QAbstractSocketPrivate();
+ virtual ~QAbstractSocketPrivate();
+
+ // from QAbstractSocketEngineReceiver
+ inline void readNotification() { canReadNotification(); }
+ inline void writeNotification() { canWriteNotification(); }
+ inline void exceptionNotification() {}
+ void connectionNotification();
+#ifndef QT_NO_NETWORKPROXY
+ inline void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) {
+ Q_Q(QAbstractSocket);
+ q->proxyAuthenticationRequired(proxy, authenticator);
+ }
+#endif
+
+ bool canReadNotification();
+ bool canWriteNotification();
+
+ // slots
+ void _q_connectToNextAddress();
+ void _q_startConnecting(const QHostInfo &hostInfo);
+ void _q_testConnection();
+ void _q_abortConnectionAttempt();
+ void _q_forceDisconnect();
+
+ bool readSocketNotifierCalled;
+ bool readSocketNotifierState;
+ bool readSocketNotifierStateSet;
+
+ bool emittedReadyRead;
+ bool emittedBytesWritten;
+
+ bool abortCalled;
+ bool closeCalled;
+ bool pendingClose;
+
+ QString hostName;
+ quint16 port;
+ QHostAddress host;
+ QList<QHostAddress> addresses;
+
+ quint16 localPort;
+ quint16 peerPort;
+ QHostAddress localAddress;
+ QHostAddress peerAddress;
+ QString peerName;
+
+ QAbstractSocketEngine *socketEngine;
+ int cachedSocketDescriptor;
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy;
+ QNetworkProxy proxyInUse;
+ void resolveProxy(const QString &hostName, quint16 port);
+#else
+ inline void resolveProxy(const QString &, quint16) { }
+#endif
+ inline void resolveProxy(quint16 port) { resolveProxy(QString(), port); }
+
+ void resetSocketLayer();
+ bool flush();
+
+ bool initSocketLayer(QAbstractSocket::NetworkLayerProtocol protocol);
+ void startConnectingByName(const QString &host);
+ void fetchConnectionParameters();
+ void setupSocketNotifiers();
+ bool readFromSocket();
+
+ qint64 readBufferMaxSize;
+ QRingBuffer readBuffer;
+ QRingBuffer writeBuffer;
+
+ bool isBuffered;
+ int blockingTimeout;
+
+ QTimer *connectTimer;
+ QTimer *disconnectTimer;
+ int connectTimeElapsed;
+
+ int hostLookupId;
+
+ QAbstractSocket::SocketType socketType;
+ QAbstractSocket::SocketState state;
+
+ QAbstractSocket::SocketError socketError;
+
+ bool prePauseReadSocketNotifierState;
+ bool prePauseWriteSocketNotifierState;
+ bool prePauseExceptionSocketNotifierState;
+ static void pauseSocketNotifiers(QAbstractSocket*);
+ static void resumeSocketNotifiers(QAbstractSocket*);
+ static QAbstractSocketEngine* getSocketEngine(QAbstractSocket*);
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTSOCKET_P_H
diff --git a/src/network/socket/qabstractsocketengine.cpp b/src/network/socket/qabstractsocketengine.cpp
new file mode 100644
index 0000000000..c29f936923
--- /dev/null
+++ b/src/network/socket/qabstractsocketengine.cpp
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractsocketengine_p.h"
+
+#ifdef Q_OS_SYMBIAN
+#include "qsymbiansocketengine_p.h"
+#else
+#include "qnativesocketengine_p.h"
+#endif
+
+#include "qmutex.h"
+#include "qnetworkproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSocketEngineHandlerList : public QList<QSocketEngineHandler*>
+{
+public:
+ QMutex mutex;
+};
+
+Q_GLOBAL_STATIC(QSocketEngineHandlerList, socketHandlers)
+
+QSocketEngineHandler::QSocketEngineHandler()
+{
+ if (!socketHandlers())
+ return;
+ QMutexLocker locker(&socketHandlers()->mutex);
+ socketHandlers()->prepend(this);
+}
+
+QSocketEngineHandler::~QSocketEngineHandler()
+{
+ if (!socketHandlers())
+ return;
+ QMutexLocker locker(&socketHandlers()->mutex);
+ socketHandlers()->removeAll(this);
+}
+
+QAbstractSocketEnginePrivate::QAbstractSocketEnginePrivate()
+ : socketError(QAbstractSocket::UnknownSocketError)
+ , hasSetSocketError(false)
+ , socketErrorString(QLatin1String(QT_TRANSLATE_NOOP(QSocketLayer, "Unknown error")))
+ , socketState(QAbstractSocket::UnconnectedState)
+ , socketType(QAbstractSocket::UnknownSocketType)
+ , socketProtocol(QAbstractSocket::UnknownNetworkLayerProtocol)
+ , localPort(0)
+ , peerPort(0)
+ , receiver(0)
+{
+}
+
+QAbstractSocketEngine::QAbstractSocketEngine(QObject *parent)
+ : QObject(*new QAbstractSocketEnginePrivate(), parent)
+{
+}
+
+QAbstractSocketEngine::QAbstractSocketEngine(QAbstractSocketEnginePrivate &dd, QObject* parent)
+ : QObject(dd, parent)
+{
+}
+
+QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &proxy, QObject *parent)
+{
+#ifndef QT_NO_NETWORKPROXY
+ // proxy type must have been resolved by now
+ if (proxy.type() == QNetworkProxy::DefaultProxy)
+ return 0;
+#endif
+
+ QMutexLocker locker(&socketHandlers()->mutex);
+ for (int i = 0; i < socketHandlers()->size(); i++) {
+ if (QAbstractSocketEngine *ret = socketHandlers()->at(i)->createSocketEngine(socketType, proxy, parent))
+ return ret;
+ }
+
+#ifndef QT_NO_NETWORKPROXY
+ // only NoProxy can have reached here
+ if (proxy.type() != QNetworkProxy::NoProxy)
+ return 0;
+#endif
+
+#ifdef Q_OS_SYMBIAN
+ return new QSymbianSocketEngine(parent);
+#else
+ return new QNativeSocketEngine(parent);
+#endif
+}
+
+QAbstractSocketEngine *QAbstractSocketEngine::createSocketEngine(int socketDescripter, QObject *parent)
+{
+ QMutexLocker locker(&socketHandlers()->mutex);
+ for (int i = 0; i < socketHandlers()->size(); i++) {
+ 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
+{
+ return d_func()->socketError;
+}
+
+QString QAbstractSocketEngine::errorString() const
+{
+ return d_func()->socketErrorString;
+}
+
+void QAbstractSocketEngine::setError(QAbstractSocket::SocketError error, const QString &errorString) const
+{
+ Q_D(const QAbstractSocketEngine);
+ d->socketError = error;
+ d->socketErrorString = errorString;
+}
+
+void QAbstractSocketEngine::setReceiver(QAbstractSocketEngineReceiver *receiver)
+{
+ d_func()->receiver = receiver;
+}
+
+void QAbstractSocketEngine::readNotification()
+{
+ if (QAbstractSocketEngineReceiver *receiver = d_func()->receiver)
+ receiver->readNotification();
+}
+
+void QAbstractSocketEngine::writeNotification()
+{
+ if (QAbstractSocketEngineReceiver *receiver = d_func()->receiver)
+ receiver->writeNotification();
+}
+
+void QAbstractSocketEngine::exceptionNotification()
+{
+ if (QAbstractSocketEngineReceiver *receiver = d_func()->receiver)
+ receiver->exceptionNotification();
+}
+
+void QAbstractSocketEngine::connectionNotification()
+{
+ if (QAbstractSocketEngineReceiver *receiver = d_func()->receiver)
+ receiver->connectionNotification();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QAbstractSocketEngine::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
+{
+ if (QAbstractSocketEngineReceiver *receiver = d_func()->receiver)
+ receiver->proxyAuthenticationRequired(proxy, authenticator);
+}
+#endif
+
+
+QAbstractSocket::SocketState QAbstractSocketEngine::state() const
+{
+ return d_func()->socketState;
+}
+
+void QAbstractSocketEngine::setState(QAbstractSocket::SocketState state)
+{
+ d_func()->socketState = state;
+}
+
+QAbstractSocket::SocketType QAbstractSocketEngine::socketType() const
+{
+ return d_func()->socketType;
+}
+
+void QAbstractSocketEngine::setSocketType(QAbstractSocket::SocketType socketType)
+{
+ d_func()->socketType = socketType;
+}
+
+QAbstractSocket::NetworkLayerProtocol QAbstractSocketEngine::protocol() const
+{
+ return d_func()->socketProtocol;
+}
+
+void QAbstractSocketEngine::setProtocol(QAbstractSocket::NetworkLayerProtocol protocol)
+{
+ d_func()->socketProtocol = protocol;
+}
+
+QHostAddress QAbstractSocketEngine::localAddress() const
+{
+ return d_func()->localAddress;
+}
+
+void QAbstractSocketEngine::setLocalAddress(const QHostAddress &address)
+{
+ d_func()->localAddress = address;
+}
+
+quint16 QAbstractSocketEngine::localPort() const
+{
+ return d_func()->localPort;
+}
+
+void QAbstractSocketEngine::setLocalPort(quint16 port)
+{
+ d_func()->localPort = port;
+}
+
+QHostAddress QAbstractSocketEngine::peerAddress() const
+{
+ return d_func()->peerAddress;
+}
+
+void QAbstractSocketEngine::setPeerAddress(const QHostAddress &address)
+{
+ d_func()->peerAddress = address;
+}
+
+quint16 QAbstractSocketEngine::peerPort() const
+{
+ return d_func()->peerPort;
+}
+
+void QAbstractSocketEngine::setPeerPort(quint16 port)
+{
+ d_func()->peerPort = port;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
new file mode 100644
index 0000000000..ee6dad60d8
--- /dev/null
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSOCKETENGINE_P_H
+#define QABSTRACTSOCKETENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtNetwork/qhostaddress.h"
+#include "QtNetwork/qabstractsocket.h"
+#include "private/qobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAuthenticator;
+class QAbstractSocketEnginePrivate;
+#ifndef QT_NO_NETWORKINTERFACE
+class QNetworkInterface;
+#endif
+class QNetworkProxy;
+
+class QAbstractSocketEngineReceiver {
+public:
+ virtual ~QAbstractSocketEngineReceiver(){}
+ virtual void readNotification()= 0;
+ virtual void writeNotification()= 0;
+ virtual void exceptionNotification()= 0;
+ virtual void connectionNotification()= 0;
+#ifndef QT_NO_NETWORKPROXY
+ virtual void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)= 0;
+#endif
+};
+
+class Q_AUTOTEST_EXPORT QAbstractSocketEngine : public QObject
+{
+ Q_OBJECT
+public:
+
+ static QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType, const QNetworkProxy &, QObject *parent);
+ static QAbstractSocketEngine *createSocketEngine(int socketDescripter, QObject *parent);
+
+ QAbstractSocketEngine(QObject *parent = 0);
+
+ enum SocketOption {
+ NonBlockingSocketOption,
+ BroadcastSocketOption,
+ ReceiveBufferSocketOption,
+ SendBufferSocketOption,
+ AddressReusable,
+ BindExclusively,
+ ReceiveOutOfBandData,
+ LowDelayOption,
+ KeepAliveOption,
+ MulticastTtlOption,
+ MulticastLoopbackOption
+ };
+
+ virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0;
+
+ virtual bool initialize(int socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState) = 0;
+
+ virtual int socketDescriptor() const = 0;
+
+ virtual bool isValid() const = 0;
+
+ virtual bool connectToHost(const QHostAddress &address, quint16 port) = 0;
+ virtual bool connectToHostByName(const QString &name, quint16 port) = 0;
+ virtual bool bind(const QHostAddress &address, quint16 port) = 0;
+ virtual bool listen() = 0;
+ virtual int accept() = 0;
+ virtual void close() = 0;
+
+ virtual qint64 bytesAvailable() const = 0;
+
+ virtual qint64 read(char *data, qint64 maxlen) = 0;
+ 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 // QT_NO_UDPSOCKET
+
+ virtual qint64 bytesToWrite() const = 0;
+
+ virtual int option(SocketOption option) const = 0;
+ virtual bool setOption(SocketOption option, int value) = 0;
+
+ virtual bool waitForRead(int msecs = 30000, bool *timedOut = 0) = 0;
+ virtual bool waitForWrite(int msecs = 30000, bool *timedOut = 0) = 0;
+ virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs = 30000, bool *timedOut = 0) = 0;
+
+ QAbstractSocket::SocketError error() const;
+ QString errorString() const;
+ QAbstractSocket::SocketState state() const;
+ QAbstractSocket::SocketType socketType() const;
+ QAbstractSocket::NetworkLayerProtocol protocol() const;
+
+ QHostAddress localAddress() const;
+ quint16 localPort() const;
+ QHostAddress peerAddress() const;
+ quint16 peerPort() const;
+
+ virtual bool isReadNotificationEnabled() const = 0;
+ virtual void setReadNotificationEnabled(bool enable) = 0;
+ virtual bool isWriteNotificationEnabled() const = 0;
+ virtual void setWriteNotificationEnabled(bool enable) = 0;
+ virtual bool isExceptionNotificationEnabled() const = 0;
+ virtual void setExceptionNotificationEnabled(bool enable) = 0;
+
+public Q_SLOTS:
+ void readNotification();
+ void writeNotification();
+ void exceptionNotification();
+ void connectionNotification();
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
+#endif
+
+public:
+ void setReceiver(QAbstractSocketEngineReceiver *receiver);
+protected:
+ QAbstractSocketEngine(QAbstractSocketEnginePrivate &dd, QObject* parent = 0);
+
+ void setError(QAbstractSocket::SocketError error, const QString &errorString) const;
+ void setState(QAbstractSocket::SocketState state);
+ void setSocketType(QAbstractSocket::SocketType socketType);
+ void setProtocol(QAbstractSocket::NetworkLayerProtocol protocol);
+ void setLocalAddress(const QHostAddress &address);
+ void setLocalPort(quint16 port);
+ void setPeerAddress(const QHostAddress &address);
+ void setPeerPort(quint16 port);
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractSocketEngine)
+ Q_DISABLE_COPY(QAbstractSocketEngine)
+};
+
+class QAbstractSocketEnginePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractSocketEngine)
+public:
+ QAbstractSocketEnginePrivate();
+
+ mutable QAbstractSocket::SocketError socketError;
+ mutable bool hasSetSocketError;
+ mutable QString socketErrorString;
+ QAbstractSocket::SocketState socketState;
+ QAbstractSocket::SocketType socketType;
+ QAbstractSocket::NetworkLayerProtocol socketProtocol;
+ QHostAddress localAddress;
+ quint16 localPort;
+ QHostAddress peerAddress;
+ quint16 peerPort;
+ QAbstractSocketEngineReceiver *receiver;
+};
+
+
+class Q_AUTOTEST_EXPORT QSocketEngineHandler
+{
+protected:
+ QSocketEngineHandler();
+ virtual ~QSocketEngineHandler();
+ virtual QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType,
+ const QNetworkProxy &, QObject *parent) = 0;
+ virtual QAbstractSocketEngine *createSocketEngine(int socketDescripter, QObject *parent) = 0;
+
+private:
+ friend class QAbstractSocketEngine;
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTSOCKETENGINE_P_H
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
new file mode 100644
index 0000000000..7846056221
--- /dev/null
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -0,0 +1,824 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhttpsocketengine_p.h"
+#include "qtcpsocket.h"
+#include "qhostaddress.h"
+#include "qurl.h"
+#include "qhttp.h"
+#include "qelapsedtimer.h"
+#include "qnetworkinterface.h"
+
+#if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP)
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+#define DEBUG
+
+QHttpSocketEngine::QHttpSocketEngine(QObject *parent)
+ : QAbstractSocketEngine(*new QHttpSocketEnginePrivate, parent)
+{
+}
+
+QHttpSocketEngine::~QHttpSocketEngine()
+{
+}
+
+bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
+{
+ Q_D(QHttpSocketEngine);
+ if (type != QAbstractSocket::TcpSocket)
+ return false;
+
+ 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.
+ d->socket->setProxy(QNetworkProxy::NoProxy);
+
+ // Intercept all the signals.
+ connect(d->socket, SIGNAL(connected()),
+ this, SLOT(slotSocketConnected()),
+ Qt::DirectConnection);
+ connect(d->socket, SIGNAL(disconnected()),
+ this, SLOT(slotSocketDisconnected()),
+ Qt::DirectConnection);
+ connect(d->socket, SIGNAL(readyRead()),
+ this, SLOT(slotSocketReadNotification()),
+ Qt::DirectConnection);
+ connect(d->socket, SIGNAL(bytesWritten(qint64)),
+ this, SLOT(slotSocketBytesWritten()),
+ Qt::DirectConnection);
+ connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(slotSocketError(QAbstractSocket::SocketError)),
+ Qt::DirectConnection);
+ connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ this, SLOT(slotSocketStateChanged(QAbstractSocket::SocketState)),
+ Qt::DirectConnection);
+
+ return true;
+}
+
+bool QHttpSocketEngine::initialize(int, QAbstractSocket::SocketState)
+{
+ return false;
+}
+
+void QHttpSocketEngine::setProxy(const QNetworkProxy &proxy)
+{
+ Q_D(QHttpSocketEngine);
+ d->proxy = proxy;
+ QString user = proxy.user();
+ if (!user.isEmpty())
+ d->authenticator.setUser(user);
+ QString password = proxy.password();
+ if (!password.isEmpty())
+ d->authenticator.setPassword(password);
+}
+
+int QHttpSocketEngine::socketDescriptor() const
+{
+ Q_D(const QHttpSocketEngine);
+ return d->socket ? d->socket->socketDescriptor() : 0;
+}
+
+bool QHttpSocketEngine::isValid() const
+{
+ Q_D(const QHttpSocketEngine);
+ return d->socket;
+}
+
+bool QHttpSocketEngine::connectInternal()
+{
+ Q_D(QHttpSocketEngine);
+
+ // If the handshake is done, enter ConnectedState state and return true.
+ if (d->state == Connected) {
+ qWarning("QHttpSocketEngine::connectToHost: called when already connected");
+ setState(QAbstractSocket::ConnectedState);
+ return true;
+ }
+
+ if (d->state == ConnectSent && d->socketState != QAbstractSocket::ConnectedState)
+ setState(QAbstractSocket::UnconnectedState);
+
+ // Handshake isn't done. If unconnected, start connecting.
+ if (d->state == None && d->socket->state() == QAbstractSocket::UnconnectedState) {
+ setState(QAbstractSocket::ConnectingState);
+ d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
+ }
+
+ // If connected (might happen right away, at least for localhost services
+ // on some BSD systems), there might already be bytes available.
+ if (bytesAvailable())
+ slotSocketReadNotification();
+
+ return d->socketState == QAbstractSocket::ConnectedState;
+}
+
+bool QHttpSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
+{
+ Q_D(QHttpSocketEngine);
+
+ setPeerAddress(address);
+ setPeerPort(port);
+ d->peerName.clear();
+
+ return connectInternal();
+}
+
+bool QHttpSocketEngine::connectToHostByName(const QString &hostname, quint16 port)
+{
+ Q_D(QHttpSocketEngine);
+
+ setPeerAddress(QHostAddress());
+ setPeerPort(port);
+ d->peerName = hostname;
+
+ return connectInternal();
+}
+
+bool QHttpSocketEngine::bind(const QHostAddress &, quint16)
+{
+ return false;
+}
+
+bool QHttpSocketEngine::listen()
+{
+ return false;
+}
+
+int QHttpSocketEngine::accept()
+{
+ return 0;
+}
+
+void QHttpSocketEngine::close()
+{
+ Q_D(QHttpSocketEngine);
+ if (d->socket) {
+ d->socket->close();
+ delete d->socket;
+ d->socket = 0;
+ }
+}
+
+qint64 QHttpSocketEngine::bytesAvailable() const
+{
+ Q_D(const QHttpSocketEngine);
+ return d->readBuffer.size() + (d->socket ? d->socket->bytesAvailable() : 0);
+}
+
+qint64 QHttpSocketEngine::read(char *data, qint64 maxlen)
+{
+ Q_D(QHttpSocketEngine);
+ qint64 bytesRead = d->socket->read(data, maxlen);
+
+ if (d->socket->state() == QAbstractSocket::UnconnectedState
+ && d->socket->bytesAvailable() == 0) {
+ emitReadNotification();
+ }
+
+ if (bytesRead == -1) {
+ // If nothing has been read so far, and the direct socket read
+ // failed, return the socket's error. Otherwise, fall through and
+ // return as much as we read so far.
+ close();
+ setError(QAbstractSocket::RemoteHostClosedError,
+ QLatin1String("Remote host closed"));
+ setState(QAbstractSocket::UnconnectedState);
+ return -1;
+ }
+ return bytesRead;
+}
+
+qint64 QHttpSocketEngine::write(const char *data, qint64 len)
+{
+ Q_D(QHttpSocketEngine);
+ return d->socket->write(data, 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 *)
+{
+ return 0;
+}
+
+qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QHostAddress &,
+ quint16)
+{
+ return 0;
+}
+
+bool QHttpSocketEngine::hasPendingDatagrams() const
+{
+ return false;
+}
+
+qint64 QHttpSocketEngine::pendingDatagramSize() const
+{
+ return 0;
+}
+#endif // QT_NO_UDPSOCKET
+
+qint64 QHttpSocketEngine::bytesToWrite() const
+{
+ Q_D(const QHttpSocketEngine);
+ if (d->socket) {
+ return d->socket->bytesToWrite();
+ } else {
+ return 0;
+ }
+}
+
+int QHttpSocketEngine::option(SocketOption option) const
+{
+ Q_D(const QHttpSocketEngine);
+ if (d->socket) {
+ // convert the enum and call the real socket
+ if (option == QAbstractSocketEngine::LowDelayOption)
+ return d->socket->socketOption(QAbstractSocket::LowDelayOption).toInt();
+ if (option == QAbstractSocketEngine::KeepAliveOption)
+ return d->socket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
+ }
+ return -1;
+}
+
+bool QHttpSocketEngine::setOption(SocketOption option, int value)
+{
+ Q_D(QHttpSocketEngine);
+ if (d->socket) {
+ // convert the enum and call the real socket
+ if (option == QAbstractSocketEngine::LowDelayOption)
+ d->socket->setSocketOption(QAbstractSocket::LowDelayOption, value);
+ if (option == QAbstractSocketEngine::KeepAliveOption)
+ d->socket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
+ return true;
+ }
+ return false;
+}
+
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
+{
+ Q_D(const QHttpSocketEngine);
+
+ if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
+ return false;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // Wait for more data if nothing is available.
+ if (!d->socket->bytesAvailable()) {
+ if (!d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ if (d->socket->state() == QAbstractSocket::UnconnectedState)
+ return true;
+ setError(d->socket->error(), d->socket->errorString());
+ if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
+ *timedOut = true;
+ return false;
+ }
+ }
+
+ // If we're not connected yet, wait until we are, or until an error
+ // occurs.
+ while (d->state != Connected && d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ // Loop while the protocol handshake is taking place.
+ }
+
+ // Report any error that may occur.
+ if (d->state != Connected) {
+ setError(d->socket->error(), d->socket->errorString());
+ if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
+ *timedOut = true;
+ return false;
+ }
+ return true;
+}
+
+bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
+{
+ Q_D(const QHttpSocketEngine);
+
+ // If we're connected, just forward the call.
+ if (d->state == Connected) {
+ if (d->socket->bytesToWrite()) {
+ if (!d->socket->waitForBytesWritten(msecs)) {
+ if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
+ *timedOut = true;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // If we're not connected yet, wait until we are, and until bytes have
+ // been received (i.e., the socket has connected, we have sent the
+ // greeting, and then received the response).
+ while (d->state != Connected && d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ // Loop while the protocol handshake is taking place.
+ }
+
+ // Report any error that may occur.
+ if (d->state != Connected) {
+// setError(d->socket->error(), d->socket->errorString());
+ if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
+ *timedOut = true;
+ }
+
+ return true;
+}
+
+bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs, bool *timedOut)
+{
+ Q_UNUSED(checkRead);
+
+ if (!checkWrite) {
+ // Not interested in writing? Then we wait for read notifications.
+ bool canRead = waitForRead(msecs, timedOut);
+ if (readyToRead)
+ *readyToRead = canRead;
+ return canRead;
+ }
+
+ // Interested in writing? Then we wait for write notifications.
+ bool canWrite = waitForWrite(msecs, timedOut);
+ if (readyToWrite)
+ *readyToWrite = canWrite;
+ return canWrite;
+}
+
+bool QHttpSocketEngine::isReadNotificationEnabled() const
+{
+ Q_D(const QHttpSocketEngine);
+ return d->readNotificationEnabled;
+}
+
+void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
+{
+ Q_D(QHttpSocketEngine);
+ if (d->readNotificationEnabled == enable)
+ return;
+
+ d->readNotificationEnabled = enable;
+ if (enable) {
+ // Enabling read notification can trigger a notification.
+ if (bytesAvailable())
+ slotSocketReadNotification();
+ }
+}
+
+bool QHttpSocketEngine::isWriteNotificationEnabled() const
+{
+ Q_D(const QHttpSocketEngine);
+ return d->writeNotificationEnabled;
+}
+
+void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
+{
+ Q_D(QHttpSocketEngine);
+ d->writeNotificationEnabled = enable;
+ if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
+ QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection);
+}
+
+bool QHttpSocketEngine::isExceptionNotificationEnabled() const
+{
+ Q_D(const QHttpSocketEngine);
+ return d->exceptNotificationEnabled;
+}
+
+void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
+{
+ Q_D(QHttpSocketEngine);
+ d->exceptNotificationEnabled = enable;
+}
+
+void QHttpSocketEngine::slotSocketConnected()
+{
+ Q_D(QHttpSocketEngine);
+
+ // Send the greeting.
+ const char method[] = "CONNECT ";
+ QByteArray peerAddress = d->peerName.isEmpty() ?
+ d->peerAddress.toString().toLatin1() :
+ QUrl::toAce(d->peerName);
+ QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
+ QByteArray data = method;
+ data += path;
+ data += " HTTP/1.1\r\n";
+ data += "Proxy-Connection: keep-alive\r\n"
+ "User-Agent: Mozilla/5.0\r\n"
+ "Host: " + peerAddress + "\r\n";
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+ //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
+ if (priv && priv->method != QAuthenticatorPrivate::None) {
+ data += "Proxy-Authorization: " + priv->calculateResponse(method, path);
+ data += "\r\n";
+ }
+ data += "\r\n";
+// qDebug() << ">>>>>>>> sending request" << this;
+// qDebug() << data;
+// qDebug() << ">>>>>>>";
+ d->socket->write(data);
+ d->state = ConnectSent;
+}
+
+void QHttpSocketEngine::slotSocketDisconnected()
+{
+}
+
+void QHttpSocketEngine::slotSocketReadNotification()
+{
+ Q_D(QHttpSocketEngine);
+ if (d->state != Connected && d->socket->bytesAvailable() == 0)
+ return;
+
+ if (d->state == Connected) {
+ // Forward as a read notification.
+ if (d->readNotificationEnabled)
+ emitReadNotification();
+ return;
+ }
+
+ readResponseContent:
+ if (d->state == ReadResponseContent) {
+ char dummybuffer[4096];
+ while (d->pendingResponseData) {
+ int read = d->socket->read(dummybuffer, qMin(sizeof(dummybuffer), (size_t)d->pendingResponseData));
+ if (read >= 0)
+ dummybuffer[read] = 0;
+
+ if (read == 0)
+ return;
+ if (read == -1) {
+ d->socket->disconnectFromHost();
+ emitWriteNotification();
+ return;
+ }
+ d->pendingResponseData -= read;
+ }
+ if (d->pendingResponseData > 0)
+ return;
+ d->state = SendAuthentication;
+ slotSocketConnected();
+ return;
+ }
+
+ // Still in handshake mode. Wait until we've got a full response.
+ bool done = false;
+ do {
+ d->readBuffer += d->socket->readLine();
+ } while (!(done = d->readBuffer.endsWith("\r\n\r\n")) && d->socket->canReadLine());
+
+ if (!done) {
+ // Wait for more.
+ return;
+ }
+
+ if (!d->readBuffer.startsWith("HTTP/1.")) {
+ // protocol error, this isn't HTTP
+ d->readBuffer.clear();
+ d->socket->close();
+ setState(QAbstractSocket::UnconnectedState);
+ setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy"));
+ emitConnectionNotification();
+ return;
+ }
+
+ QHttpResponseHeader responseHeader(QString::fromLatin1(d->readBuffer));
+ d->readBuffer.clear(); // we parsed the proxy protocol response. from now on direct socket reading will be done
+
+ int statusCode = responseHeader.statusCode();
+ if (statusCode == 200) {
+ d->state = Connected;
+ setLocalAddress(d->socket->localAddress());
+ setLocalPort(d->socket->localPort());
+ setState(QAbstractSocket::ConnectedState);
+ } else if (statusCode == 407) {
+ if (d->authenticator.isNull())
+ d->authenticator.detach();
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+
+ priv->parseHttpResponse(responseHeader, true);
+
+ if (priv->phase == QAuthenticatorPrivate::Invalid) {
+ // problem parsing the reply
+ d->socket->close();
+ setState(QAbstractSocket::UnconnectedState);
+ setError(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy"));
+ emitConnectionNotification();
+ return;
+ }
+
+ bool willClose;
+ QString proxyConnectionHeader = responseHeader.value(QLatin1String("Proxy-Connection"));
+ proxyConnectionHeader = proxyConnectionHeader.toLower();
+ if (proxyConnectionHeader == QLatin1String("close")) {
+ willClose = true;
+ } else if (proxyConnectionHeader == QLatin1String("keep-alive")) {
+ willClose = false;
+ } else {
+ // no Proxy-Connection header, so use the default
+ // HTTP 1.1's default behaviour is to keep persistent connections
+ // HTTP 1.0 or earlier, so we expect the server to close
+ willClose = (responseHeader.majorVersion() * 0x100 + responseHeader.minorVersion()) <= 0x0100;
+ }
+
+ if (willClose) {
+ // the server will disconnect, so let's avoid receiving an error
+ // especially since the signal below may trigger a new event loop
+ d->socket->disconnectFromHost();
+ d->socket->readAll();
+ }
+
+ if (priv->phase == QAuthenticatorPrivate::Done)
+ emit proxyAuthenticationRequired(d->proxy, &d->authenticator);
+
+ // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
+ if (priv->phase == QAuthenticatorPrivate::Done) {
+ setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
+ d->socket->disconnectFromHost();
+ } else {
+ // close the connection if it isn't already and reconnect using the chosen authentication method
+ d->state = SendAuthentication;
+ if (willClose) {
+ d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
+ } else {
+ bool ok;
+ int contentLength = responseHeader.value(QLatin1String("Content-Length")).toInt(&ok);
+ if (ok && contentLength > 0) {
+ d->state = ReadResponseContent;
+ d->pendingResponseData = contentLength;
+ goto readResponseContent;
+ } else {
+ d->state = SendAuthentication;
+ slotSocketConnected();
+ }
+ }
+ return;
+ }
+ } else {
+ d->socket->close();
+ setState(QAbstractSocket::UnconnectedState);
+ if (statusCode == 403 || statusCode == 405) {
+ // 403 Forbidden
+ // 405 Method Not Allowed
+ setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection"));
+ } else if (statusCode == 404) {
+ // 404 Not Found: host lookup error
+ setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
+ } else if (statusCode == 503) {
+ // 503 Service Unavailable: Connection Refused
+ setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused"));
+ } else {
+ // Some other reply
+ //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
+ setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy"));
+ }
+ }
+
+ // The handshake is done; notify that we're connected (or failed to connect)
+ emitConnectionNotification();
+}
+
+void QHttpSocketEngine::slotSocketBytesWritten()
+{
+ Q_D(QHttpSocketEngine);
+ if (d->state == Connected && d->writeNotificationEnabled)
+ emitWriteNotification();
+}
+
+void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
+{
+ Q_D(QHttpSocketEngine);
+ d->readBuffer.clear();
+
+ if (d->state != Connected) {
+ // we are in proxy handshaking stages
+ if (error == QAbstractSocket::HostNotFoundError)
+ setError(QAbstractSocket::ProxyNotFoundError, tr("Proxy server not found"));
+ else if (error == QAbstractSocket::ConnectionRefusedError)
+ setError(QAbstractSocket::ProxyConnectionRefusedError, tr("Proxy connection refused"));
+ else if (error == QAbstractSocket::SocketTimeoutError)
+ setError(QAbstractSocket::ProxyConnectionTimeoutError, tr("Proxy server connection timed out"));
+ else if (error == QAbstractSocket::RemoteHostClosedError)
+ setError(QAbstractSocket::ProxyConnectionClosedError, tr("Proxy connection closed prematurely"));
+ else
+ setError(error, d->socket->errorString());
+ emitConnectionNotification();
+ return;
+ }
+
+ // We're connected
+ if (error == QAbstractSocket::SocketTimeoutError)
+ return; // ignore this error
+
+ d->state = None;
+ setError(error, d->socket->errorString());
+ 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)
+{
+ Q_UNUSED(state);
+}
+
+void QHttpSocketEngine::emitPendingReadNotification()
+{
+ Q_D(QHttpSocketEngine);
+ d->readNotificationPending = false;
+ if (d->readNotificationEnabled)
+ emit readNotification();
+}
+
+void QHttpSocketEngine::emitPendingWriteNotification()
+{
+ Q_D(QHttpSocketEngine);
+ d->writeNotificationPending = false;
+ if (d->writeNotificationEnabled)
+ emit writeNotification();
+}
+
+void QHttpSocketEngine::emitPendingConnectionNotification()
+{
+ Q_D(QHttpSocketEngine);
+ d->connectionNotificationPending = false;
+ emit connectionNotification();
+}
+
+void QHttpSocketEngine::emitReadNotification()
+{
+ Q_D(QHttpSocketEngine);
+ d->readNotificationActivated = true;
+ // 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);
+ }
+}
+
+void QHttpSocketEngine::emitWriteNotification()
+{
+ Q_D(QHttpSocketEngine);
+ d->writeNotificationActivated = true;
+ if (d->writeNotificationEnabled && !d->writeNotificationPending) {
+ d->writeNotificationPending = true;
+ QMetaObject::invokeMethod(this, "emitPendingWriteNotification", Qt::QueuedConnection);
+ }
+}
+
+void QHttpSocketEngine::emitConnectionNotification()
+{
+ Q_D(QHttpSocketEngine);
+ if (!d->connectionNotificationPending) {
+ d->connectionNotificationPending = true;
+ QMetaObject::invokeMethod(this, "emitPendingConnectionNotification", Qt::QueuedConnection);
+ }
+}
+
+QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
+ : readNotificationEnabled(false)
+ , writeNotificationEnabled(false)
+ , exceptNotificationEnabled(false)
+ , readNotificationActivated(false)
+ , writeNotificationActivated(false)
+ , readNotificationPending(false)
+ , writeNotificationPending(false)
+ , connectionNotificationPending(false)
+ , pendingResponseData(0)
+{
+ socket = 0;
+ state = QHttpSocketEngine::None;
+}
+
+QHttpSocketEnginePrivate::~QHttpSocketEnginePrivate()
+{
+}
+
+QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
+ const QNetworkProxy &proxy,
+ QObject *parent)
+{
+ if (socketType != QAbstractSocket::TcpSocket)
+ return 0;
+
+ // proxy type must have been resolved by now
+ if (proxy.type() != QNetworkProxy::HttpProxy)
+ return 0;
+
+ // we only accept active sockets
+ if (!qobject_cast<QAbstractSocket *>(parent))
+ return 0;
+
+ QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
+ engine->setProxy(proxy);
+ return engine;
+}
+
+QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(int, QObject *)
+{
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
new file mode 100644
index 0000000000..361ef5c693
--- /dev/null
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPSOCKETENGINE_P_H
+#define QHTTPSOCKETENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractsocketengine_p.h"
+#include "qabstractsocket.h"
+#include "qnetworkproxy.h"
+#include "private/qauthenticator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP)
+
+class QTcpSocket;
+class QHttpSocketEnginePrivate;
+
+class Q_AUTOTEST_EXPORT QHttpSocketEngine : public QAbstractSocketEngine
+{
+ Q_OBJECT
+public:
+ enum HttpState {
+ None,
+ ConnectSent,
+ Connected,
+ SendAuthentication,
+ ReadResponseContent
+ };
+ QHttpSocketEngine(QObject *parent = 0);
+ ~QHttpSocketEngine();
+
+ bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol);
+ bool initialize(int socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState);
+
+ void setProxy(const QNetworkProxy &networkProxy);
+
+ int socketDescriptor() const;
+
+ bool isValid() const;
+
+ bool connectInternal();
+ 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();
+
+ qint64 bytesAvailable() const;
+
+ qint64 read(char *data, qint64 maxlen);
+ 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,
+ quint16 port);
+ bool hasPendingDatagrams() const;
+ qint64 pendingDatagramSize() const;
+#endif // QT_NO_UDPSOCKET
+
+ qint64 bytesToWrite() const;
+
+ 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);
+
+public slots:
+ void slotSocketConnected();
+ void slotSocketDisconnected();
+ void slotSocketReadNotification();
+ void slotSocketBytesWritten();
+ void slotSocketError(QAbstractSocket::SocketError error);
+ void slotSocketStateChanged(QAbstractSocket::SocketState state);
+
+private slots:
+ void emitPendingReadNotification();
+ void emitPendingWriteNotification();
+ void emitPendingConnectionNotification();
+
+private:
+ void emitReadNotification();
+ void emitWriteNotification();
+ void emitConnectionNotification();
+
+ Q_DECLARE_PRIVATE(QHttpSocketEngine)
+ Q_DISABLE_COPY(QHttpSocketEngine)
+
+};
+
+
+class QHttpSocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QHttpSocketEngine)
+public:
+ QHttpSocketEnginePrivate();
+ ~QHttpSocketEnginePrivate();
+
+ QNetworkProxy proxy;
+ QString peerName;
+ QTcpSocket *socket;
+ QByteArray readBuffer; // only used for parsing the proxy response
+ QHttpSocketEngine::HttpState state;
+ QAuthenticator authenticator;
+ bool readNotificationEnabled;
+ bool writeNotificationEnabled;
+ bool exceptNotificationEnabled;
+ bool readNotificationActivated;
+ bool writeNotificationActivated;
+ bool readNotificationPending;
+ bool writeNotificationPending;
+ bool connectionNotificationPending;
+ uint pendingResponseData;
+};
+
+class Q_AUTOTEST_EXPORT QHttpSocketEngineHandler : public QSocketEngineHandler
+{
+public:
+ virtual QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType,
+ const QNetworkProxy &, QObject *parent);
+ virtual QAbstractSocketEngine *createSocketEngine(int socketDescripter, QObject *parent);
+};
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QHTTPSOCKETENGINE_H
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
new file mode 100644
index 0000000000..46822d7bad
--- /dev/null
+++ b/src/network/socket/qlocalserver.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalserver.h"
+#include "qlocalserver_p.h"
+#include "qlocalsocket.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_LOCALSERVER
+
+/*!
+ \class QLocalServer
+ \since 4.4
+
+ \brief The QLocalServer class provides a local socket based server.
+
+ This class makes it possible to accept incoming local socket
+ connections.
+
+ Call listen() to have the server start listening
+ for incoming connections on a specified key. The
+ newConnection() signal is then emitted each time a client
+ connects to the server.
+
+ Call nextPendingConnection() to accept the pending connection
+ as a connected QLocalSocket. The function returns a pointer to a
+ QLocalSocket that can be used for communicating with the client.
+
+ If an error occurs, serverError() returns the type of error, and
+ errorString() can be called to get a human readable description
+ of what happened.
+
+ When listening for connections, the name which the server is
+ listening on is available through serverName().
+
+ Calling close() makes QLocalServer stop listening for incoming connections.
+
+ Although QLocalServer is designed for use with an event loop, it's possible
+ to use it without one. In that case, you must use waitForNewConnection(),
+ which blocks until either a connection is available or a timeout expires.
+
+ \sa QLocalSocket, QTcpServer
+*/
+
+/*!
+ Create a new local socket server with the given \a parent.
+
+ \sa listen()
+ */
+QLocalServer::QLocalServer(QObject *parent)
+ : QObject(*new QLocalServerPrivate, parent)
+{
+ Q_D(QLocalServer);
+ d->init();
+}
+
+/*!
+ Destroys the QLocalServer object. If the server is listening for
+ connections, it is automatically closed.
+
+ Any client QLocalSockets that are still connected must either
+ disconnect or be reparented before the server is deleted.
+
+ \sa close()
+ */
+QLocalServer::~QLocalServer()
+{
+ if (isListening())
+ close();
+}
+
+/*!
+ Stop listening for incoming connections. Existing connections are not
+ effected, but any new connections will be refused.
+
+ \sa isListening(), listen()
+ */
+void QLocalServer::close()
+{
+ Q_D(QLocalServer);
+ if (!isListening())
+ return;
+ qDeleteAll(d->pendingConnections);
+ d->pendingConnections.clear();
+ d->closeServer();
+ d->serverName.clear();
+ d->fullServerName.clear();
+ d->errorString.clear();
+ d->error = QAbstractSocket::UnknownSocketError;
+}
+
+/*!
+ Returns the human-readable message appropriate to the current error
+ reported by serverError(). If no suitable string is available, an empty
+ string is returned.
+
+ \sa serverError()
+ */
+QString QLocalServer::errorString() const
+{
+ Q_D(const QLocalServer);
+ return d->errorString;
+}
+
+/*!
+ Returns true if the server has a pending connection; otherwise
+ returns false.
+
+ \sa nextPendingConnection(), setMaxPendingConnections()
+ */
+bool QLocalServer::hasPendingConnections() const
+{
+ Q_D(const QLocalServer);
+ return !(d->pendingConnections.isEmpty());
+}
+
+/*!
+ This virtual function is called by QLocalServer when a new connection
+ is available. \a socketDescriptor is the native socket descriptor for
+ the accepted connection.
+
+ The base implementation creates a QLocalSocket, sets the socket descriptor
+ and then stores the QLocalSocket in an internal list of pending
+ connections. Finally newConnection() is emitted.
+
+ Reimplement this function to alter the server's behavior
+ when a connection is available.
+
+ \sa newConnection(), nextPendingConnection(),
+ QLocalSocket::setSocketDescriptor()
+ */
+void QLocalServer::incomingConnection(quintptr socketDescriptor)
+{
+ Q_D(QLocalServer);
+ QLocalSocket *socket = new QLocalSocket(this);
+ socket->setSocketDescriptor(socketDescriptor);
+ d->pendingConnections.enqueue(socket);
+ emit newConnection();
+}
+
+/*!
+ Returns true if the server is listening for incoming connections
+ otherwise false.
+
+ \sa listen(), close()
+ */
+bool QLocalServer::isListening() const
+{
+ Q_D(const QLocalServer);
+ return !(d->serverName.isEmpty());
+}
+
+/*!
+ Tells the server to listen for incoming connections on \a name.
+ If the server is currently listening then it will return false.
+ Return true on success otherwise false.
+
+ \a name can be a single name and QLocalServer will determine
+ the correct platform specific path. serverName() will return
+ the name that is passed into listen.
+
+ Usually you would just pass in a name like "foo", but on Unix this
+ could also be a path such as "/tmp/foo" and on Windows this could
+ be a pipe path such as "\\\\.\\pipe\\foo"
+
+ Note:
+ On Unix if the server crashes without closing listen will fail
+ with AddressInUseError. To create a new server the file should be removed.
+ On Windows two local servers can listen to the same pipe at the same
+ time, but any connections will go to one of the server.
+
+ \sa serverName(), isListening(), close()
+ */
+bool QLocalServer::listen(const QString &name)
+{
+ Q_D(QLocalServer);
+ if (isListening()) {
+ qWarning("QLocalServer::listen() called when already listening");
+ return false;
+ }
+
+ if (name.isEmpty()) {
+ d->error = QAbstractSocket::HostNotFoundError;
+ QString function = QLatin1String("QLocalServer::listen");
+ d->errorString = tr("%1: Name error").arg(function);
+ return false;
+ }
+
+ if (!d->listen(name)) {
+ d->serverName.clear();
+ d->fullServerName.clear();
+ return false;
+ }
+
+ d->serverName = name;
+ return true;
+}
+
+/*!
+ Returns the maximum number of pending accepted connections.
+ The default is 30.
+
+ \sa setMaxPendingConnections(), hasPendingConnections()
+ */
+int QLocalServer::maxPendingConnections() const
+{
+ Q_D(const QLocalServer);
+ return d->maxPendingConnections;
+}
+
+/*!
+ \fn void QLocalServer::newConnection()
+
+ This signal is emitted every time a new connection is available.
+
+ \sa hasPendingConnections(), nextPendingConnection()
+*/
+
+/*!
+ Returns the next pending connection as a connected QLocalSocket object.
+
+ The socket is created as a child of the server, which means that it is
+ automatically deleted when the QLocalServer object is destroyed. It is
+ still a good idea to delete the object explicitly when you are done with
+ it, to avoid wasting memory.
+
+ 0 is returned if this function is called when there are no pending
+ connections.
+
+ \sa hasPendingConnections(), newConnection(), incomingConnection()
+ */
+QLocalSocket *QLocalServer::nextPendingConnection()
+{
+ Q_D(QLocalServer);
+ 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
+ if (d->pendingConnections.size() <= d->maxPendingConnections)
+#ifndef Q_OS_WIN
+ d->socketNotifier->setEnabled(true);
+#else
+ d->connectionEventNotifier->setEnabled(true);
+#endif
+#endif
+ return nextSocket;
+}
+
+/*!
+ \since 4.5
+
+ Removes any server instance that might cause a call to listen() to fail
+ and returns true if successful; otherwise returns false.
+ This function is meant to recover from a crash, when the previous server
+ instance has not been cleaned up.
+
+ On Windows, this function does nothing; on Unix, it removes the socket file
+ given by \a name.
+
+ \warning Be careful to avoid removing sockets of running instances.
+*/
+bool QLocalServer::removeServer(const QString &name)
+{
+ return QLocalServerPrivate::removeServer(name);
+}
+
+/*!
+ Returns the server name if the server is listening for connections;
+ otherwise returns QString()
+
+ \sa listen(), fullServerName()
+ */
+QString QLocalServer::serverName() const
+{
+ Q_D(const QLocalServer);
+ return d->serverName;
+}
+
+/*!
+ Returns the full path that the server is listening on.
+
+ Note: This is platform specific
+
+ \sa listen(), serverName()
+ */
+QString QLocalServer::fullServerName() const
+{
+ Q_D(const QLocalServer);
+ return d->fullServerName;
+}
+
+/*!
+ Returns the type of error that occurred last or NoError.
+
+ \sa errorString()
+ */
+QAbstractSocket::SocketError QLocalServer::serverError() const
+{
+ Q_D(const QLocalServer);
+ return d->error;
+}
+
+/*!
+ Sets the maximum number of pending accepted connections to
+ \a numConnections. QLocalServer will accept no more than
+ \a numConnections incoming connections before nextPendingConnection()
+ is called.
+
+ Note: Even though QLocalServer will stop accepting new connections
+ after it has reached its maximum number of pending connections,
+ the operating system may still keep them in queue which will result
+ in clients signaling that it is connected.
+
+ \sa maxPendingConnections(), hasPendingConnections()
+ */
+void QLocalServer::setMaxPendingConnections(int numConnections)
+{
+ Q_D(QLocalServer);
+ d->maxPendingConnections = numConnections;
+}
+
+/*!
+ Waits for at most \a msec milliseconds or until an incoming connection
+ is available. Returns true if a connection is available; otherwise
+ returns false. If the operation timed out and \a timedOut is not 0,
+ *timedOut will be set to true.
+
+ This is a blocking function call. Its use is ill-advised in a
+ single-threaded GUI application, since the whole application will stop
+ responding until the function returns. waitForNewConnection() is mostly
+ useful when there is no event loop available.
+
+ The non-blocking alternative is to connect to the newConnection() signal.
+
+ If msec is -1, this function will not time out.
+
+ \sa hasPendingConnections(), nextPendingConnection()
+ */
+bool QLocalServer::waitForNewConnection(int msec, bool *timedOut)
+{
+ Q_D(QLocalServer);
+ if (timedOut)
+ *timedOut = false;
+
+ if (!isListening())
+ return false;
+
+ d->waitForNewConnection(msec, timedOut);
+
+ return !d->pendingConnections.isEmpty();
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qlocalserver.cpp"
+
diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h
new file mode 100644
index 0000000000..e8dc1c9b70
--- /dev/null
+++ b/src/network/socket/qlocalserver.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOCALSERVER_H
+#define QLOCALSERVER_H
+
+#include <QtNetwork/qabstractsocket.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_LOCALSERVER
+
+class QLocalSocket;
+class QLocalServerPrivate;
+
+class Q_NETWORK_EXPORT QLocalServer : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QLocalServer)
+
+Q_SIGNALS:
+ void newConnection();
+
+public:
+ QLocalServer(QObject *parent = 0);
+ ~QLocalServer();
+
+ void close();
+ QString errorString() const;
+ virtual bool hasPendingConnections() const;
+ bool isListening() const;
+ bool listen(const QString &name);
+ int maxPendingConnections() const;
+ virtual QLocalSocket *nextPendingConnection();
+ QString serverName() const;
+ QString fullServerName() const;
+ static bool removeServer(const QString &name);
+ QAbstractSocket::SocketError serverError() const;
+ void setMaxPendingConnections(int numConnections);
+ bool waitForNewConnection(int msec = 0, bool *timedOut = 0);
+
+protected:
+ virtual void incomingConnection(quintptr socketDescriptor);
+
+private:
+ Q_DISABLE_COPY(QLocalServer)
+ Q_PRIVATE_SLOT(d_func(), void _q_onNewConnection())
+};
+
+#endif // QT_NO_LOCALSERVER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLOCALSERVER_H
+
diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h
new file mode 100644
index 0000000000..1ee5df2558
--- /dev/null
+++ b/src/network/socket/qlocalserver_p.h
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOCALSERVER_P_H
+#define QLOCALSERVER_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 QLocalServer class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_LOCALSERVER
+
+#include "qlocalserver.h"
+#include "private/qobject_p.h"
+#include <qqueue.h>
+
+#if defined(QT_LOCALSOCKET_TCP)
+# include <qtcpserver.h>
+#elif defined(Q_OS_WIN)
+# include <qt_windows.h>
+# include <private/qwineventnotifier_p.h>
+#else
+# include <private/qabstractsocketengine_p.h>
+# include <qsocketnotifier.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QLocalServerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QLocalServer)
+
+public:
+ QLocalServerPrivate() :
+#if !defined(QT_LOCALSOCKET_TCP) && !defined(Q_OS_WIN)
+ listenSocket(-1), socketNotifier(0),
+#endif
+ maxPendingConnections(30), error(QAbstractSocket::UnknownSocketError)
+ {
+ }
+
+ void init();
+ bool listen(const QString &name);
+ static bool removeServer(const QString &name);
+ void closeServer();
+ void waitForNewConnection(int msec, bool *timedOut);
+ void _q_onNewConnection();
+
+#if defined(QT_LOCALSOCKET_TCP)
+
+ QTcpServer tcpServer;
+ QMap<quintptr, QTcpSocket*> socketMap;
+#elif defined(Q_OS_WIN)
+ struct Listener {
+ HANDLE handle;
+ OVERLAPPED overlapped;
+ bool connected;
+ };
+
+ void setError(const QString &function);
+ bool addListener();
+
+ QList<Listener> listeners;
+ HANDLE eventHandle;
+ QWinEventNotifier *connectionEventNotifier;
+#else
+ void setError(const QString &function);
+
+ int listenSocket;
+ QSocketNotifier *socketNotifier;
+#endif
+
+ QString serverName;
+ QString fullServerName;
+ int maxPendingConnections;
+ QQueue<QLocalSocket*> pendingConnections;
+ QString errorString;
+ QAbstractSocket::SocketError error;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LOCALSERVER
+
+#endif // QLOCALSERVER_P_H
+
diff --git a/src/network/socket/qlocalserver_tcp.cpp b/src/network/socket/qlocalserver_tcp.cpp
new file mode 100644
index 0000000000..aeda8635af
--- /dev/null
+++ b/src/network/socket/qlocalserver_tcp.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalserver.h"
+#include "qlocalserver_p.h"
+#include "qlocalsocket.h"
+#include "qlocalsocket_p.h"
+
+#include <qhostaddress.h>
+#include <qsettings.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+void QLocalServerPrivate::init()
+{
+ Q_Q(QLocalServer);
+ q->connect(&tcpServer, SIGNAL(newConnection()), SLOT(_q_onNewConnection()));
+}
+
+bool QLocalServerPrivate::listen(const QString &requestedServerName)
+{
+ if (!tcpServer.listen(QHostAddress::LocalHost))
+ return false;
+
+ const QLatin1String prefix("QLocalServer/");
+ if (requestedServerName.startsWith(prefix))
+ fullServerName = requestedServerName;
+ else
+ fullServerName = prefix + requestedServerName;
+
+ QSettings settings(QLatin1String("Trolltech"), QLatin1String("Qt"));
+ if (settings.contains(fullServerName)) {
+ qWarning("QLocalServer::listen: server name is already in use.");
+ tcpServer.close();
+ return false;
+ }
+
+ settings.setValue(fullServerName, tcpServer.serverPort());
+ return true;
+}
+
+void QLocalServerPrivate::closeServer()
+{
+ QSettings settings(QLatin1String("Trolltech"), QLatin1String("Qt"));
+ if (fullServerName == QLatin1String("QLocalServer"))
+ settings.setValue(fullServerName, QVariant());
+ else
+ settings.remove(fullServerName);
+ tcpServer.close();
+}
+
+void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
+{
+ if (pendingConnections.isEmpty())
+ tcpServer.waitForNewConnection(msec, timedOut);
+ else if (timedOut)
+ *timedOut = false;
+}
+
+void QLocalServerPrivate::_q_onNewConnection()
+{
+ Q_Q(QLocalServer);
+ QTcpSocket* tcpSocket = tcpServer.nextPendingConnection();
+ if (!tcpSocket) {
+ qWarning("QLocalServer: no pending connection");
+ return;
+ }
+
+ tcpSocket->setParent(q);
+ const quintptr socketDescriptor = tcpSocket->socketDescriptor();
+ q->incomingConnection(socketDescriptor);
+}
+
+bool QLocalServerPrivate::removeServer(const QString &name)
+{
+ const QLatin1String prefix("QLocalServer/");
+ QString serverName;
+ if (name.startsWith(prefix))
+ serverName = name;
+ else
+ serverName = prefix + name;
+
+ QSettings settings(QLatin1String("Trolltech"), QLatin1String("Qt"));
+ if (settings.contains(serverName))
+ settings.remove(serverName);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp
new file mode 100644
index 0000000000..bc07fcf235
--- /dev/null
+++ b/src/network/socket/qlocalserver_unix.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalserver.h"
+#include "qlocalserver_p.h"
+#include "qlocalsocket.h"
+#include "qlocalsocket_p.h"
+#include "qnet_unix_p.h"
+
+#ifndef QT_NO_LOCALSERVER
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <qdebug.h>
+#include <qdir.h>
+#include <qdatetime.h>
+
+#ifdef Q_OS_VXWORKS
+# include <selectLib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+void QLocalServerPrivate::init()
+{
+}
+
+bool QLocalServerPrivate::removeServer(const QString &name)
+{
+ QString fileName;
+ if (name.startsWith(QLatin1Char('/'))) {
+ fileName = name;
+ } else {
+ fileName = QDir::cleanPath(QDir::tempPath());
+ fileName += QLatin1Char('/') + name;
+ }
+ if (QFile::exists(fileName))
+ return QFile::remove(fileName);
+ else
+ return true;
+}
+
+bool QLocalServerPrivate::listen(const QString &requestedServerName)
+{
+ Q_Q(QLocalServer);
+
+ // determine the full server path
+ if (requestedServerName.startsWith(QLatin1Char('/'))) {
+ fullServerName = requestedServerName;
+ } else {
+ fullServerName = QDir::cleanPath(QDir::tempPath());
+ fullServerName += QLatin1Char('/') + requestedServerName;
+ }
+ serverName = requestedServerName;
+
+ // create the unix socket
+ listenSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0);
+ if (-1 == listenSocket) {
+ setError(QLatin1String("QLocalServer::listen"));
+ closeServer();
+ return false;
+ }
+
+ // Construct the unix address
+ struct ::sockaddr_un addr;
+ addr.sun_family = PF_UNIX;
+ if (sizeof(addr.sun_path) < (uint)fullServerName.toLatin1().size() + 1) {
+ setError(QLatin1String("QLocalServer::listen"));
+ closeServer();
+ return false;
+ }
+ ::memcpy(addr.sun_path, fullServerName.toLatin1().data(),
+ fullServerName.toLatin1().size() + 1);
+
+#ifdef Q_OS_SYMBIAN
+ // In SYMBIAN OS it can currently happen that accept is called twice,
+ // once from waitForNewConnection and once via QSocketNotfier activity
+ //
+ // As an workaround, we set the socket to non blocking so possible
+ // subsequent call to accept will not block in any case
+ //
+ // This change can be removed once more generic fix to select thread
+ // synchronization problem is implemented.
+ int flags = fcntl(listenSocket, F_GETFL, 0);
+ if (-1 == flags
+ || -1 == (fcntl(listenSocket, F_SETFL, flags | O_NONBLOCK))) {
+ setError(QLatin1String("QLocalServer::listen"));
+ closeServer();
+ return false;
+ }
+#endif
+
+ // bind
+ if(-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un))) {
+ setError(QLatin1String("QLocalServer::listen"));
+ // if address is in use already, just close the socket, but do not delete the file
+ if(errno == EADDRINUSE)
+ QT_CLOSE(listenSocket);
+ // otherwise, close the socket and delete the file
+ else
+ closeServer();
+ listenSocket = -1;
+ return false;
+ }
+
+ // listen for connections
+ if (-1 == qt_safe_listen(listenSocket, 50)) {
+ setError(QLatin1String("QLocalServer::listen"));
+ closeServer();
+ listenSocket = -1;
+ if (error != QAbstractSocket::AddressInUseError)
+ QFile::remove(fullServerName);
+ return false;
+ }
+ Q_ASSERT(!socketNotifier);
+ socketNotifier = new QSocketNotifier(listenSocket,
+ QSocketNotifier::Read, q);
+ q->connect(socketNotifier, SIGNAL(activated(int)),
+ q, SLOT(_q_onNewConnection()));
+ socketNotifier->setEnabled(maxPendingConnections > 0);
+ return true;
+}
+
+/*!
+ \internal
+
+ \sa QLocalServer::closeServer()
+ */
+void QLocalServerPrivate::closeServer()
+{
+ if (-1 != listenSocket)
+ QT_CLOSE(listenSocket);
+ listenSocket = -1;
+
+ if (socketNotifier) {
+ socketNotifier->setEnabled(false); // Otherwise, closed socket is checked before deleter runs
+ socketNotifier->deleteLater();
+ socketNotifier = 0;
+ }
+
+ if (!fullServerName.isEmpty())
+ QFile::remove(fullServerName);
+}
+
+/*!
+ \internal
+
+ We have received a notification that we can read on the listen socket.
+ Accept the new socket.
+ */
+void QLocalServerPrivate::_q_onNewConnection()
+{
+ Q_Q(QLocalServer);
+ if (-1 == listenSocket)
+ return;
+
+ ::sockaddr_un addr;
+ QT_SOCKLEN_T length = sizeof(sockaddr_un);
+ int connectedSocket = qt_safe_accept(listenSocket, (sockaddr *)&addr, &length);
+ if(-1 == connectedSocket) {
+ setError(QLatin1String("QLocalSocket::activated"));
+ closeServer();
+ } else {
+ socketNotifier->setEnabled(pendingConnections.size()
+ <= maxPendingConnections);
+ q->incomingConnection(connectedSocket);
+ }
+}
+
+void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
+{
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(listenSocket, &readfds);
+
+ timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+
+ int result = -1;
+ result = qt_safe_select(listenSocket + 1, &readfds, 0, 0, (msec == -1) ? 0 : &timeout);
+ if (-1 == result) {
+ setError(QLatin1String("QLocalServer::waitForNewConnection"));
+ closeServer();
+ }
+ if (result > 0)
+ _q_onNewConnection();
+ if (timedOut)
+ *timedOut = (result == 0);
+}
+
+void QLocalServerPrivate::setError(const QString &function)
+{
+ if (EAGAIN == errno)
+ return;
+
+ switch (errno) {
+ case EACCES:
+ errorString = QLocalServer::tr("%1: Permission denied").arg(function);
+ error = QAbstractSocket::SocketAccessError;
+ break;
+ case ELOOP:
+ case ENOENT:
+ case ENAMETOOLONG:
+ case EROFS:
+ case ENOTDIR:
+ errorString = QLocalServer::tr("%1: Name error").arg(function);
+ error = QAbstractSocket::HostNotFoundError;
+ break;
+ case EADDRINUSE:
+ errorString = QLocalServer::tr("%1: Address in use").arg(function);
+ error = QAbstractSocket::AddressInUseError;
+ break;
+
+ default:
+ errorString = QLocalServer::tr("%1: Unknown error %2")
+ .arg(function).arg(errno);
+ error = QAbstractSocket::UnknownSocketError;
+#if defined QLOCALSERVER_DEBUG
+ qWarning() << errorString << "fullServerName:" << fullServerName;
+#endif
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LOCALSERVER
diff --git a/src/network/socket/qlocalserver_win.cpp b/src/network/socket/qlocalserver_win.cpp
new file mode 100644
index 0000000000..fb1015792f
--- /dev/null
+++ b/src/network/socket/qlocalserver_win.cpp
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalserver.h"
+#include "qlocalserver_p.h"
+#include "qlocalsocket.h"
+
+#include <qdebug.h>
+
+// The buffer size need to be 0 otherwise data could be
+// lost if the socket that has written data closes the connection
+// before it is read. Pipewriter is used for write buffering.
+#define BUFSIZE 0
+
+// ###: This should be a property. Should replace the insane 50 on unix as well.
+#define SYSTEM_MAX_PENDING_SOCKETS 8
+
+QT_BEGIN_NAMESPACE
+
+bool QLocalServerPrivate::addListener()
+{
+ // The object must not change its address once the
+ // contained OVERLAPPED struct is passed to Windows.
+ listeners << Listener();
+ Listener &listener = listeners.last();
+
+ listener.handle = CreateNamedPipe(
+ (const wchar_t *)fullServerName.utf16(), // pipe name
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
+ PIPE_TYPE_BYTE | // byte type pipe
+ PIPE_READMODE_BYTE | // byte-read mode
+ PIPE_WAIT, // blocking mode
+ PIPE_UNLIMITED_INSTANCES, // max. instances
+ BUFSIZE, // output buffer size
+ BUFSIZE, // input buffer size
+ 3000, // client time-out
+ NULL);
+
+ if (listener.handle == INVALID_HANDLE_VALUE) {
+ setError(QLatin1String("QLocalServerPrivate::addListener"));
+ listeners.removeLast();
+ return false;
+ }
+
+ memset(&listener.overlapped, 0, sizeof(listener.overlapped));
+ listener.overlapped.hEvent = eventHandle;
+ if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) {
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING:
+ listener.connected = false;
+ break;
+ case ERROR_PIPE_CONNECTED:
+ listener.connected = true;
+ SetEvent(eventHandle);
+ break;
+ default:
+ CloseHandle(listener.handle);
+ setError(QLatin1String("QLocalServerPrivate::addListener"));
+ listeners.removeLast();
+ return false;
+ }
+ } else {
+ Q_ASSERT_X(false, "QLocalServerPrivate::addListener", "The impossible happened");
+ SetEvent(eventHandle);
+ }
+ return true;
+}
+
+void QLocalServerPrivate::setError(const QString &function)
+{
+ int windowsError = GetLastError();
+ errorString = QString::fromLatin1("%1: %2").arg(function).arg(qt_error_string(windowsError));
+ error = QAbstractSocket::UnknownSocketError;
+}
+
+void QLocalServerPrivate::init()
+{
+}
+
+bool QLocalServerPrivate::removeServer(const QString &name)
+{
+ Q_UNUSED(name);
+ return true;
+}
+
+bool QLocalServerPrivate::listen(const QString &name)
+{
+ Q_Q(QLocalServer);
+
+ QString pipePath = QLatin1String("\\\\.\\pipe\\");
+ if (name.startsWith(pipePath))
+ fullServerName = name;
+ else
+ fullServerName = pipePath + name;
+
+ // Use only one event for all listeners of one socket.
+ // The idea is that listener events are rare, so polling all listeners once in a while is
+ // cheap compared to waiting for N additional events in each iteration of the main loop.
+ eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
+ connectionEventNotifier = new QWinEventNotifier(eventHandle , q);
+ q->connect(connectionEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onNewConnection()));
+
+ for (int i = 0; i < SYSTEM_MAX_PENDING_SOCKETS; ++i)
+ if (!addListener())
+ return false;
+ return true;
+}
+
+void QLocalServerPrivate::_q_onNewConnection()
+{
+ Q_Q(QLocalServer);
+ DWORD dummy;
+
+ // Reset first, otherwise we could reset an event which was asserted
+ // immediately after we checked the conn status.
+ ResetEvent(eventHandle);
+
+ // Testing shows that there is indeed absolutely no guarantee which listener gets
+ // a client connection first, so there is no way around polling all of them.
+ for (int i = 0; i < listeners.size(); ) {
+ HANDLE handle = listeners[i].handle;
+ if (listeners[i].connected
+ || GetOverlappedResult(handle, &listeners[i].overlapped, &dummy, FALSE))
+ {
+ listeners.removeAt(i);
+
+ addListener();
+
+ if (pendingConnections.size() > maxPendingConnections)
+ connectionEventNotifier->setEnabled(false);
+
+ // Make this the last thing so connected slots can wreak the least havoc
+ q->incomingConnection((quintptr)handle);
+ } else {
+ if (GetLastError() != ERROR_IO_INCOMPLETE) {
+ q->close();
+ setError(QLatin1String("QLocalServerPrivate::_q_onNewConnection"));
+ return;
+ }
+
+ ++i;
+ }
+ }
+}
+
+void QLocalServerPrivate::closeServer()
+{
+ connectionEventNotifier->setEnabled(false); // Otherwise, closed handle is checked before deleter runs
+ connectionEventNotifier->deleteLater();
+ connectionEventNotifier = 0;
+ CloseHandle(eventHandle);
+ for (int i = 0; i < listeners.size(); ++i)
+ CloseHandle(listeners[i].handle);
+ listeners.clear();
+}
+
+void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut)
+{
+ Q_Q(QLocalServer);
+ if (!pendingConnections.isEmpty() || !q->isListening())
+ return;
+
+ DWORD result = WaitForSingleObject(eventHandle, (msecs == -1) ? INFINITE : msecs);
+ if (result == WAIT_TIMEOUT) {
+ if (timedOut)
+ *timedOut = true;
+ } else {
+ _q_onNewConnection();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp
new file mode 100644
index 0000000000..9a2b0ba3cd
--- /dev/null
+++ b/src/network/socket/qlocalsocket.cpp
@@ -0,0 +1,507 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalsocket.h"
+#include "qlocalsocket_p.h"
+
+#ifndef QT_NO_LOCALSOCKET
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QLocalSocket
+ \since 4.4
+
+ \brief The QLocalSocket class provides a local socket.
+
+ On Windows this is a named pipe and on Unix this is a local domain socket.
+
+ If an error occurs, socketError() returns the type of error, and
+ errorString() can be called to get a human readable description
+ of what happened.
+
+ Although QLocalSocket is designed for use with an event loop, it's possible
+ to use it without one. In that case, you must use waitForConnected(),
+ waitForReadyRead(), waitForBytesWritten(), and waitForDisconnected()
+ which blocks until the operation is complete or the timeout expires.
+
+ Note that this feature is not supported on versions of Windows earlier than
+ Windows XP.
+
+ \sa QLocalServer
+*/
+
+/*!
+ \fn void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
+
+ Attempts to make a connection to \a name.
+
+ The socket is opened in the given \a openMode and first enters ConnectingState.
+ It then attempts to connect to the address or addresses returned by the lookup.
+ Finally, if a connection is established, QLocalSocket enters ConnectedState
+ and emits connected().
+
+ At any point, the socket can emit error() to signal that an error occurred.
+
+ See also state(), serverName(), and waitForConnected().
+*/
+
+/*!
+ \fn void QLocalSocket::connected()
+
+ This signal is emitted after connectToServer() has been called and
+ a connection has been successfully established.
+
+ \sa connectToServer(), disconnected()
+*/
+
+/*!
+ \fn bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
+ LocalSocketState socketState, OpenMode openMode)
+
+ Initializes QLocalSocket with the native socket descriptor
+ \a socketDescriptor. Returns true if socketDescriptor is accepted
+ as a valid socket descriptor; otherwise returns false. The socket is
+ opened in the mode specified by \a openMode, and enters the socket state
+ specified by \a socketState.
+
+ \note It is not possible to initialize two local sockets with the same
+ native socket descriptor.
+
+ \sa socketDescriptor(), state(), openMode()
+*/
+
+/*!
+ \fn quintptr QLocalSocket::socketDescriptor() const
+
+ Returns the native socket descriptor of the QLocalSocket object if
+ this is available; otherwise returns -1.
+
+ The socket descriptor is not available when QLocalSocket
+ is in UnconnectedState.
+
+ \sa setSocketDescriptor()
+*/
+
+/*!
+ \fn qint64 QLocalSocket::readData(char *data, qint64 c)
+ \reimp
+*/
+
+/*!
+ \fn qint64 QLocalSocket::writeData(const char *data, qint64 c)
+ \reimp
+*/
+
+/*!
+ \fn void QLocalSocket::abort()
+
+ Aborts the current connection and resets the socket.
+ Unlike disconnectFromServer(), this function immediately closes the socket,
+ clearing any pending data in the write buffer.
+
+ \sa disconnectFromServer(), close()
+*/
+
+/*!
+ \fn qint64 QLocalSocket::bytesAvailable() const
+ \reimp
+*/
+
+/*!
+ \fn qint64 QLocalSocket::bytesToWrite() const
+ \reimp
+*/
+
+/*!
+ \fn bool QLocalSocket::canReadLine() const
+ \reimp
+*/
+
+/*!
+ \fn void QLocalSocket::close()
+ \reimp
+*/
+
+/*!
+ \fn bool QLocalSocket::waitForBytesWritten(int msecs)
+ \reimp
+*/
+
+/*!
+ \fn bool QLocalSocket::flush()
+
+ This function writes as much as possible from the internal write buffer
+ to the socket, without blocking. If any data was written, this function
+ returns true; otherwise false is returned.
+
+ Call this function if you need QLocalSocket to start sending buffered data
+ immediately. The number of bytes successfully written depends on the
+ operating system. In most cases, you do not need to call this function,
+ because QLocalSocket will start sending data automatically once control
+ goes back to the event loop. In the absence of an event loop, call
+ waitForBytesWritten() instead.
+
+ \sa write(), waitForBytesWritten()
+*/
+
+/*!
+ \fn void QLocalSocket::disconnectFromServer()
+
+ Attempts to close the socket. If there is pending data waiting to be
+ written, QLocalSocket will enter ClosingState and wait until all data
+ has been written. Eventually, it will enter UnconnectedState and emit
+ the disconnectedFromServer() signal.
+
+ \sa connectToServer()
+*/
+
+/*!
+ \fn QLocalSocket::LocalSocketError QLocalSocket::error() const
+
+ Returns the type of error that last occurred.
+
+ \sa state(), errorString()
+*/
+
+/*!
+ \fn bool QLocalSocket::isValid() const
+
+ Returns true if the socket is valid and ready for use; otherwise
+ returns false.
+
+ \note The socket's state must be ConnectedState before reading
+ and writing can occur.
+
+ \sa state(), connectToServer()
+*/
+
+/*!
+ \fn qint64 QLocalSocket::readBufferSize() const
+
+ Returns the size of the internal read buffer. This limits the amount of
+ data that the client can receive before you call read() or readAll().
+ A read buffer size of 0 (the default) means that the buffer has no size
+ limit, ensuring that no data is lost.
+
+ \sa setReadBufferSize(), read()
+*/
+
+/*!
+ \fn void QLocalSocket::setReadBufferSize(qint64 size)
+
+ Sets the size of QLocalSocket's internal read buffer to be \a size bytes.
+
+ If the buffer size is limited to a certain size, QLocalSocket won't
+ buffer more than this size of data. Exceptionally, a buffer size of 0
+ means that the read buffer is unlimited and all incoming data is buffered.
+ This is the default.
+
+ This option is useful if you only read the data at certain points in
+ time (e.g., in a real-time streaming application) or if you want to
+ protect your socket against receiving too much data, which may eventually
+ cause your application to run out of memory.
+
+ \sa readBufferSize(), read()
+*/
+
+/*!
+ \fn bool QLocalSocket::waitForConnected(int msecs)
+
+ Waits until the socket is connected, up to \a msecs milliseconds. If the
+ connection has been established, this function returns true; otherwise
+ it returns false. In the case where it returns false, you can call
+ error() to determine the cause of the error.
+
+ The following example waits up to one second for a connection
+ to be established:
+
+ \snippet doc/src/snippets/code/src_network_socket_qlocalsocket_unix.cpp 0
+
+ If \a msecs is -1, this function will not time out.
+
+ \sa connectToServer(), connected()
+*/
+
+/*!
+ \fn bool QLocalSocket::waitForDisconnected(int msecs)
+
+ Waits until the socket has disconnected, up to \a msecs
+ milliseconds. If the connection has been disconnected, this
+ function returns true; otherwise it returns false. In the case
+ where it returns false, you can call error() to determine
+ the cause of the error.
+
+ The following example waits up to one second for a connection
+ to be closed:
+
+ \snippet doc/src/snippets/code/src_network_socket_qlocalsocket_unix.cpp 1
+
+ If \a msecs is -1, this function will not time out.
+
+ \sa disconnectFromServer(), close()
+*/
+
+/*!
+ \fn bool QLocalSocket::waitForReadyRead(int msecs)
+
+ This function blocks until data is available for reading and the
+ \l{QIODevice::}{readyRead()} signal has been emitted. The function
+ will timeout after \a msecs milliseconds; the default timeout is
+ 30000 milliseconds.
+
+ The function returns true if data is available for reading;
+ otherwise it returns false (if an error occurred or the
+ operation timed out).
+
+ \sa waitForBytesWritten()
+*/
+
+/*!
+ \fn void QLocalSocket::disconnected()
+
+ This signal is emitted when the socket has been disconnected.
+
+ \sa connectToServer(), disconnectFromServer(), abort(), connected()
+*/
+
+/*!
+ \fn void QLocalSocket::error(QLocalSocket::LocalSocketError socketError)
+
+ This signal is emitted after an error occurred. The \a socketError
+ parameter describes the type of error that occurred.
+
+ QLocalSocket::LocalSocketError is not a registered metatype, so for queued
+ connections, you will have to register it with Q_DECLARE_METATYPE() and
+ qRegisterMetaType().
+
+ \sa error(), errorString(), {Creating Custom Qt Types}
+*/
+
+/*!
+ \fn void QLocalSocket::stateChanged(QLocalSocket::LocalSocketState socketState)
+
+ This signal is emitted whenever QLocalSocket's state changes.
+ The \a socketState parameter is the new state.
+
+ QLocalSocket::SocketState is not a registered metatype, so for queued
+ connections, you will have to register it with Q_DECLARE_METATYPE() and
+ qRegisterMetaType().
+
+ \sa state(), {Creating Custom Qt Types}
+*/
+
+/*!
+ Creates a new local socket. The \a parent argument is passed to
+ QObject's constructor.
+ */
+QLocalSocket::QLocalSocket(QObject * parent)
+ : QIODevice(*new QLocalSocketPrivate, parent)
+{
+ Q_D(QLocalSocket);
+ d->init();
+}
+
+/*!
+ Destroys the socket, closing the connection if necessary.
+ */
+QLocalSocket::~QLocalSocket()
+{
+ close();
+#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP)
+ Q_D(QLocalSocket);
+ d->unixSocket.setParent(0);
+#endif
+}
+
+/*!
+ Returns the name of the peer as specified by connectToServer(), or an
+ empty QString if connectToServer() has not been called or it failed.
+
+ \sa connectToServer(), fullServerName()
+
+ */
+QString QLocalSocket::serverName() const
+{
+ Q_D(const QLocalSocket);
+ return d->serverName;
+}
+
+/*!
+ Returns the server path that the socket is connected to.
+
+ \note The return value of this function is platform specific.
+
+ \sa connectToServer(), serverName()
+ */
+QString QLocalSocket::fullServerName() const
+{
+ Q_D(const QLocalSocket);
+ return d->fullServerName;
+}
+
+/*!
+ Returns the state of the socket.
+
+ \sa error()
+ */
+QLocalSocket::LocalSocketState QLocalSocket::state() const
+{
+ Q_D(const QLocalSocket);
+ return d->state;
+}
+
+/*! \reimp
+*/
+bool QLocalSocket::isSequential() const
+{
+ return true;
+}
+
+/*!
+ \enum QLocalSocket::LocalSocketError
+
+ The LocalServerError enumeration represents the errors that can occur.
+ The most recent error can be retrieved through a call to
+ \l QLocalSocket::error().
+
+ \value ConnectionRefusedError The connection was refused by
+ the peer (or timed out).
+ \value PeerClosedError The remote socket closed the connection.
+ Note that the client socket (i.e., this socket) will be closed
+ after the remote close notification has been sent.
+ \value ServerNotFoundError The local socket name was not found.
+ \value SocketAccessError The socket operation failed because the
+ application lacked the required privileges.
+ \value SocketResourceError The local system ran out of resources
+ (e.g., too many sockets).
+ \value SocketTimeoutError The socket operation timed out.
+ \value DatagramTooLargeError The datagram was larger than the operating
+ system's limit (which can be as low as 8192 bytes).
+ \value ConnectionError An error occurred with the connection.
+ \value UnsupportedSocketOperationError The requested socket operation
+ is not supported by the local operating system.
+ \value UnknownSocketError An unidentified error occurred.
+ */
+
+/*!
+ \enum QLocalSocket::LocalSocketState
+
+ This enum describes the different states in which a socket can be.
+
+ \sa QLocalSocket::state()
+
+ \value UnconnectedState The socket is not connected.
+ \value ConnectingState The socket has started establishing a connection.
+ \value ConnectedState A connection is established.
+ \value ClosingState The socket is about to close
+ (data may still be waiting to be written).
+ */
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, QLocalSocket::LocalSocketError error)
+{
+ switch (error) {
+ case QLocalSocket::ConnectionRefusedError:
+ debug << "QLocalSocket::ConnectionRefusedError";
+ break;
+ case QLocalSocket::PeerClosedError:
+ debug << "QLocalSocket::PeerClosedError";
+ break;
+ case QLocalSocket::ServerNotFoundError:
+ debug << "QLocalSocket::ServerNotFoundError";
+ break;
+ case QLocalSocket::SocketAccessError:
+ debug << "QLocalSocket::SocketAccessError";
+ break;
+ case QLocalSocket::SocketResourceError:
+ debug << "QLocalSocket::SocketResourceError";
+ break;
+ case QLocalSocket::SocketTimeoutError:
+ debug << "QLocalSocket::SocketTimeoutError";
+ break;
+ case QLocalSocket::DatagramTooLargeError:
+ debug << "QLocalSocket::DatagramTooLargeError";
+ break;
+ case QLocalSocket::ConnectionError:
+ debug << "QLocalSocket::ConnectionError";
+ break;
+ case QLocalSocket::UnsupportedSocketOperationError:
+ debug << "QLocalSocket::UnsupportedSocketOperationError";
+ break;
+ case QLocalSocket::UnknownSocketError:
+ debug << "QLocalSocket::UnknownSocketError";
+ break;
+ default:
+ debug << "QLocalSocket::SocketError(" << int(error) << ')';
+ break;
+ }
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, QLocalSocket::LocalSocketState state)
+{
+ switch (state) {
+ case QLocalSocket::UnconnectedState:
+ debug << "QLocalSocket::UnconnectedState";
+ break;
+ case QLocalSocket::ConnectingState:
+ debug << "QLocalSocket::ConnectingState";
+ break;
+ case QLocalSocket::ConnectedState:
+ debug << "QLocalSocket::ConnectedState";
+ break;
+ case QLocalSocket::ClosingState:
+ debug << "QLocalSocket::ClosingState";
+ break;
+ default:
+ debug << "QLocalSocket::SocketState(" << int(state) << ')';
+ break;
+ }
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif
+
+#include "moc_qlocalsocket.cpp"
diff --git a/src/network/socket/qlocalsocket.h b/src/network/socket/qlocalsocket.h
new file mode 100644
index 0000000000..2f99b55a70
--- /dev/null
+++ b/src/network/socket/qlocalsocket.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOCALSOCKET_H
+#define QLOCALSOCKET_H
+
+#include <QtCore/qiodevice.h>
+#include <QtNetwork/qabstractsocket.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_LOCALSOCKET
+
+class QLocalSocketPrivate;
+
+class Q_NETWORK_EXPORT QLocalSocket : public QIODevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QLocalSocket)
+
+public:
+ enum LocalSocketError
+ {
+ ConnectionRefusedError = QAbstractSocket::ConnectionRefusedError,
+ PeerClosedError = QAbstractSocket::RemoteHostClosedError,
+ ServerNotFoundError = QAbstractSocket::HostNotFoundError,
+ SocketAccessError = QAbstractSocket::SocketAccessError,
+ SocketResourceError = QAbstractSocket::SocketResourceError,
+ SocketTimeoutError = QAbstractSocket::SocketTimeoutError,
+ DatagramTooLargeError = QAbstractSocket::DatagramTooLargeError,
+ ConnectionError = QAbstractSocket::NetworkError,
+ UnsupportedSocketOperationError = QAbstractSocket::UnsupportedSocketOperationError,
+ UnknownSocketError = QAbstractSocket::UnknownSocketError
+ };
+
+ enum LocalSocketState
+ {
+ UnconnectedState = QAbstractSocket::UnconnectedState,
+ ConnectingState = QAbstractSocket::ConnectingState,
+ ConnectedState = QAbstractSocket::ConnectedState,
+ ClosingState = QAbstractSocket::ClosingState
+ };
+
+ QLocalSocket(QObject *parent = 0);
+ ~QLocalSocket();
+
+ void connectToServer(const QString &name, OpenMode openMode = ReadWrite);
+ void disconnectFromServer();
+
+ QString serverName() const;
+ QString fullServerName() const;
+
+ void abort();
+ virtual bool isSequential() const;
+ virtual qint64 bytesAvailable() const;
+ virtual qint64 bytesToWrite() const;
+ virtual bool canReadLine() const;
+ virtual void close();
+ LocalSocketError error() const;
+ bool flush();
+ bool isValid() const;
+ qint64 readBufferSize() const;
+ void setReadBufferSize(qint64 size);
+
+ bool setSocketDescriptor(quintptr socketDescriptor,
+ LocalSocketState socketState = ConnectedState,
+ OpenMode openMode = ReadWrite);
+ quintptr socketDescriptor() const;
+
+ LocalSocketState state() const;
+ bool waitForBytesWritten(int msecs = 30000);
+ bool waitForConnected(int msecs = 30000);
+ bool waitForDisconnected(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000);
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+ void error(QLocalSocket::LocalSocketError socketError);
+ void stateChanged(QLocalSocket::LocalSocketState socketState);
+
+protected:
+ virtual qint64 readData(char*, qint64);
+ virtual qint64 writeData(const char*, qint64);
+
+private:
+ Q_DISABLE_COPY(QLocalSocket)
+#if defined(QT_LOCALSOCKET_TCP)
+ Q_PRIVATE_SLOT(d_func(), void _q_stateChanged(QAbstractSocket::SocketState))
+ Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError))
+#elif defined(Q_OS_WIN)
+ Q_PRIVATE_SLOT(d_func(), void _q_notified())
+ Q_PRIVATE_SLOT(d_func(), void _q_canWrite())
+ Q_PRIVATE_SLOT(d_func(), void _q_pipeClosed())
+ Q_PRIVATE_SLOT(d_func(), void _q_emitReadyRead())
+#else
+ Q_PRIVATE_SLOT(d_func(), void _q_stateChanged(QAbstractSocket::SocketState))
+ Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_connectToSocket())
+ Q_PRIVATE_SLOT(d_func(), void _q_abortConnectionAttempt())
+#endif
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+Q_NETWORK_EXPORT QDebug operator<<(QDebug, QLocalSocket::LocalSocketError);
+Q_NETWORK_EXPORT QDebug operator<<(QDebug, QLocalSocket::LocalSocketState);
+#endif
+
+#endif // QT_NO_LOCALSOCKET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QLOCALSOCKET_H
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
new file mode 100644
index 0000000000..09e50f512f
--- /dev/null
+++ b/src/network/socket/qlocalsocket_p.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOCALSOCKET_P_H
+#define QLOCALSOCKET_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 QLocalSocket class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_LOCALSOCKET
+
+#include "qlocalsocket.h"
+#include "private/qiodevice_p.h"
+
+#include <qtimer.h>
+
+#if defined(QT_LOCALSOCKET_TCP)
+# include "qtcpsocket.h"
+#elif defined(Q_OS_WIN)
+# include "private/qwindowspipewriter_p.h"
+# include "private/qringbuffer_p.h"
+# include <private/qwineventnotifier_p.h>
+#else
+# include "private/qabstractsocketengine_p.h"
+# include <qtcpsocket.h>
+# include <qsocketnotifier.h>
+# include <errno.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(Q_OS_WIN) || defined(QT_LOCALSOCKET_TCP)
+class QLocalUnixSocket : public QTcpSocket
+{
+
+public:
+ QLocalUnixSocket() : QTcpSocket()
+ {
+ };
+
+ inline void setSocketState(QAbstractSocket::SocketState state)
+ {
+ QTcpSocket::setSocketState(state);
+ };
+
+ inline void setErrorString(const QString &string)
+ {
+ QTcpSocket::setErrorString(string);
+ }
+
+ inline void setSocketError(QAbstractSocket::SocketError error)
+ {
+ QTcpSocket::setSocketError(error);
+ }
+
+ inline qint64 readData(char *data, qint64 maxSize)
+ {
+ return QTcpSocket::readData(data, maxSize);
+ }
+
+ inline qint64 writeData(const char *data, qint64 maxSize)
+ {
+ return QTcpSocket::writeData(data, maxSize);
+ }
+};
+#endif //#if !defined(Q_OS_WIN) || defined(QT_LOCALSOCKET_TCP)
+
+class QLocalSocketPrivate : public QIODevicePrivate
+{
+ Q_DECLARE_PUBLIC(QLocalSocket)
+
+public:
+ QLocalSocketPrivate();
+ void init();
+
+#if defined(QT_LOCALSOCKET_TCP)
+ QLocalUnixSocket* tcpSocket;
+ bool ownsTcpSocket;
+ void setSocket(QLocalUnixSocket*);
+ QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const;
+ void errorOccurred(QLocalSocket::LocalSocketError, const QString &function);
+ void _q_stateChanged(QAbstractSocket::SocketState newState);
+ void _q_error(QAbstractSocket::SocketError newError);
+#elif defined(Q_OS_WIN)
+ ~QLocalSocketPrivate();
+ void destroyPipeHandles();
+ void setErrorString(const QString &function);
+ void _q_notified();
+ void _q_canWrite();
+ void _q_pipeClosed();
+ void _q_emitReadyRead();
+ DWORD checkPipeState();
+ void startAsyncRead();
+ bool completeAsyncRead();
+ void checkReadyRead();
+ HANDLE handle;
+ OVERLAPPED overlapped;
+ QWindowsPipeWriter *pipeWriter;
+ qint64 readBufferMaxSize;
+ QRingBuffer readBuffer;
+ int actualReadBufferSize;
+ QWinEventNotifier *dataReadNotifier;
+ QLocalSocket::LocalSocketError error;
+ bool readSequenceStarted;
+ bool pendingReadyRead;
+ bool pipeClosed;
+ static const qint64 initialReadBufferSize = 4096;
+#else
+ QLocalUnixSocket unixSocket;
+ QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const;
+ void errorOccurred(QLocalSocket::LocalSocketError, const QString &function);
+ void _q_stateChanged(QAbstractSocket::SocketState newState);
+ void _q_error(QAbstractSocket::SocketError newError);
+ void _q_connectToSocket();
+ void _q_abortConnectionAttempt();
+ void cancelDelayedConnect();
+ QSocketNotifier *delayConnect;
+ QTimer *connectTimer;
+ int connectingSocket;
+ QString connectingName;
+ QIODevice::OpenMode connectingOpenMode;
+#endif
+
+ QString serverName;
+ QString fullServerName;
+ QLocalSocket::LocalSocketState state;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_LOCALSOCKET
+
+#endif // QLOCALSOCKET_P_H
+
diff --git a/src/network/socket/qlocalsocket_tcp.cpp b/src/network/socket/qlocalsocket_tcp.cpp
new file mode 100644
index 0000000000..182d64aeb3
--- /dev/null
+++ b/src/network/socket/qlocalsocket_tcp.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalsocket.h"
+#include "qlocalsocket_p.h"
+#include "qlocalserver.h"
+
+#include <qhostaddress.h>
+#include <qsettings.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
+ tcpSocket(0),
+ ownsTcpSocket(true),
+ state(QLocalSocket::UnconnectedState)
+{
+}
+
+void QLocalSocketPrivate::init()
+{
+ setSocket(new QLocalUnixSocket);
+}
+
+void QLocalSocketPrivate::setSocket(QLocalUnixSocket* socket)
+{
+ if (ownsTcpSocket)
+ delete tcpSocket;
+ ownsTcpSocket = false;
+ tcpSocket = socket;
+
+ Q_Q(QLocalSocket);
+ // QIODevice signals
+ q->connect(tcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
+ q->connect(tcpSocket, SIGNAL(bytesWritten(qint64)),
+ q, SIGNAL(bytesWritten(qint64)));
+ q->connect(tcpSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
+ // QAbstractSocket signals
+ q->connect(tcpSocket, SIGNAL(connected()), q, SIGNAL(connected()));
+ q->connect(tcpSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
+ q->connect(tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
+ q->connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ q, SLOT(_q_error(QAbstractSocket::SocketError)));
+ q->connect(tcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
+ tcpSocket->setParent(q);
+}
+
+void QLocalSocketPrivate::_q_error(QAbstractSocket::SocketError socketError)
+{
+ Q_Q(QLocalSocket);
+ QString function = QLatin1String("QLocalSocket");
+ QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
+ QString errorString = generateErrorString(error, function);
+ q->setErrorString(errorString);
+ emit q->error(error);
+}
+
+void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
+{
+ Q_Q(QLocalSocket);
+ QLocalSocket::LocalSocketState currentState = state;
+ switch(newState) {
+ case QAbstractSocket::UnconnectedState:
+ state = QLocalSocket::UnconnectedState;
+ serverName.clear();
+ fullServerName.clear();
+ break;
+ case QAbstractSocket::ConnectingState:
+ state = QLocalSocket::ConnectingState;
+ break;
+ case QAbstractSocket::ConnectedState:
+ state = QLocalSocket::ConnectedState;
+ break;
+ case QAbstractSocket::ClosingState:
+ state = QLocalSocket::ClosingState;
+ break;
+ default:
+#if defined QLOCALSOCKET_DEBUG
+ qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
+#endif
+ return;
+ }
+ if (currentState != state)
+ emit q->stateChanged(state);
+}
+
+QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
+{
+ QString errorString;
+ switch (error) {
+ case QLocalSocket::ConnectionRefusedError:
+ errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
+ break;
+ case QLocalSocket::PeerClosedError:
+ errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
+ break;
+ case QLocalSocket::ServerNotFoundError:
+ errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
+ break;
+ case QLocalSocket::SocketAccessError:
+ errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
+ break;
+ case QLocalSocket::SocketResourceError:
+ errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
+ break;
+ case QLocalSocket::SocketTimeoutError:
+ errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
+ break;
+ case QLocalSocket::DatagramTooLargeError:
+ errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
+ break;
+ case QLocalSocket::ConnectionError:
+ errorString = QLocalSocket::tr("%1: Connection error").arg(function);
+ break;
+ case QLocalSocket::UnsupportedSocketOperationError:
+ errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
+ break;
+ case QLocalSocket::UnknownSocketError:
+ default:
+ errorString = QLocalSocket::tr("%1: Unknown error").arg(function);
+ }
+ return errorString;
+}
+
+void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, const QString &function)
+{
+ Q_Q(QLocalSocket);
+ switch (error) {
+ case QLocalSocket::ConnectionRefusedError:
+ tcpSocket->setSocketError(QAbstractSocket::ConnectionRefusedError);
+ break;
+ case QLocalSocket::PeerClosedError:
+ tcpSocket->setSocketError(QAbstractSocket::RemoteHostClosedError);
+ break;
+ case QLocalSocket::ServerNotFoundError:
+ tcpSocket->setSocketError(QAbstractSocket::HostNotFoundError);
+ break;
+ case QLocalSocket::SocketAccessError:
+ tcpSocket->setSocketError(QAbstractSocket::SocketAccessError);
+ break;
+ case QLocalSocket::SocketResourceError:
+ tcpSocket->setSocketError(QAbstractSocket::SocketResourceError);
+ break;
+ case QLocalSocket::SocketTimeoutError:
+ tcpSocket->setSocketError(QAbstractSocket::SocketTimeoutError);
+ break;
+ case QLocalSocket::DatagramTooLargeError:
+ tcpSocket->setSocketError(QAbstractSocket::DatagramTooLargeError);
+ break;
+ case QLocalSocket::ConnectionError:
+ tcpSocket->setSocketError(QAbstractSocket::NetworkError);
+ break;
+ case QLocalSocket::UnsupportedSocketOperationError:
+ tcpSocket->setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
+ break;
+ case QLocalSocket::UnknownSocketError:
+ default:
+ tcpSocket->setSocketError(QAbstractSocket::UnknownSocketError);
+ }
+
+ QString errorString = generateErrorString(error, function);
+ q->setErrorString(errorString);
+ emit q->error(error);
+
+ // errors cause a disconnect
+ tcpSocket->setSocketState(QAbstractSocket::UnconnectedState);
+ bool stateChanged = (state != QLocalSocket::UnconnectedState);
+ state = QLocalSocket::UnconnectedState;
+ q->close();
+ if (stateChanged)
+ q->emit stateChanged(state);
+}
+
+void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ if (state() == ConnectedState
+ || state() == ConnectingState)
+ return;
+
+ d->errorString.clear();
+ d->state = ConnectingState;
+ emit stateChanged(d->state);
+
+ if (name.isEmpty()) {
+ d->errorOccurred(ServerNotFoundError,
+ QLatin1String("QLocalSocket::connectToServer"));
+ return;
+ }
+
+ d->serverName = name;
+ const QLatin1String prefix("QLocalServer/");
+ if (name.startsWith(prefix))
+ d->fullServerName = name;
+ else
+ d->fullServerName = prefix + name;
+
+ QSettings settings(QLatin1String("Trolltech"), QLatin1String("Qt"));
+ bool ok;
+ const quint16 port = settings.value(d->fullServerName).toUInt(&ok);
+ if (!ok) {
+ d->errorOccurred(ServerNotFoundError,
+ QLatin1String("QLocalSocket::connectToServer"));
+ return;
+ }
+ d->tcpSocket->connectToHost(QHostAddress::LocalHost, port, openMode);
+ QIODevice::open(openMode);
+}
+
+bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
+ LocalSocketState socketState, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
+ switch (socketState) {
+ case ConnectingState:
+ newSocketState = QAbstractSocket::ConnectingState;
+ break;
+ case ConnectedState:
+ newSocketState = QAbstractSocket::ConnectedState;
+ break;
+ case ClosingState:
+ newSocketState = QAbstractSocket::ClosingState;
+ break;
+ case UnconnectedState:
+ newSocketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+ QIODevice::open(openMode);
+ d->state = socketState;
+
+ // Is our parent a localServer? Then it wants us to use its remote socket.
+ QLocalServer* localServer = qobject_cast<QLocalServer*>( parent() );
+ if (localServer) {
+ foreach (QObject* child, localServer->children()) {
+ QTcpSocket* childTcpSocket = qobject_cast<QTcpSocket*>(child);
+ if (childTcpSocket && childTcpSocket->socketDescriptor() == socketDescriptor) {
+ d->setSocket( static_cast<QLocalUnixSocket*>(childTcpSocket) );
+ return true;
+ }
+ }
+ }
+
+ // We couldn't find the socket in the children list of our server.
+ // So it might be that the user wants to set a socket descriptor.
+ return d->tcpSocket->setSocketDescriptor(socketDescriptor,
+ newSocketState, openMode);
+}
+
+quintptr QLocalSocket::socketDescriptor() const
+{
+ Q_D(const QLocalSocket);
+ return d->tcpSocket->socketDescriptor();
+}
+
+qint64 QLocalSocket::readData(char *data, qint64 c)
+{
+ Q_D(QLocalSocket);
+ return d->tcpSocket->readData(data, c);
+}
+
+qint64 QLocalSocket::writeData(const char *data, qint64 c)
+{
+ Q_D(QLocalSocket);
+ return d->tcpSocket->writeData(data, c);
+}
+
+void QLocalSocket::abort()
+{
+ Q_D(QLocalSocket);
+ d->tcpSocket->abort();
+}
+
+qint64 QLocalSocket::bytesAvailable() const
+{
+ Q_D(const QLocalSocket);
+ return QIODevice::bytesAvailable() + d->tcpSocket->bytesAvailable();
+}
+
+qint64 QLocalSocket::bytesToWrite() const
+{
+ Q_D(const QLocalSocket);
+ return d->tcpSocket->bytesToWrite();
+}
+
+bool QLocalSocket::canReadLine() const
+{
+ Q_D(const QLocalSocket);
+ return QIODevice::canReadLine() || d->tcpSocket->canReadLine();
+}
+
+void QLocalSocket::close()
+{
+ Q_D(QLocalSocket);
+ d->tcpSocket->close();
+ d->serverName.clear();
+ d->fullServerName.clear();
+ QIODevice::close();
+}
+
+bool QLocalSocket::waitForBytesWritten(int msecs)
+{
+ Q_D(QLocalSocket);
+ return d->tcpSocket->waitForBytesWritten(msecs);
+}
+
+bool QLocalSocket::flush()
+{
+ Q_D(QLocalSocket);
+ return d->tcpSocket->flush();
+}
+
+void QLocalSocket::disconnectFromServer()
+{
+ Q_D(QLocalSocket);
+ d->tcpSocket->disconnectFromHost();
+}
+
+QLocalSocket::LocalSocketError QLocalSocket::error() const
+{
+ Q_D(const QLocalSocket);
+ switch (d->tcpSocket->error()) {
+ case QAbstractSocket::ConnectionRefusedError:
+ return QLocalSocket::ConnectionRefusedError;
+ case QAbstractSocket::RemoteHostClosedError:
+ return QLocalSocket::PeerClosedError;
+ case QAbstractSocket::HostNotFoundError:
+ return QLocalSocket::ServerNotFoundError;
+ case QAbstractSocket::SocketAccessError:
+ return QLocalSocket::SocketAccessError;
+ case QAbstractSocket::SocketResourceError:
+ return QLocalSocket::SocketResourceError;
+ case QAbstractSocket::SocketTimeoutError:
+ return QLocalSocket::SocketTimeoutError;
+ case QAbstractSocket::DatagramTooLargeError:
+ return QLocalSocket::DatagramTooLargeError;
+ case QAbstractSocket::NetworkError:
+ return QLocalSocket::ConnectionError;
+ case QAbstractSocket::UnsupportedSocketOperationError:
+ return QLocalSocket::UnsupportedSocketOperationError;
+ case QAbstractSocket::UnknownSocketError:
+ return QLocalSocket::UnknownSocketError;
+ default:
+#if defined QLOCALSOCKET_DEBUG
+ qWarning() << "QLocalSocket error not handled:" << d->tcpSocket->error();
+#endif
+ break;
+ }
+ return UnknownSocketError;
+}
+
+bool QLocalSocket::isValid() const
+{
+ Q_D(const QLocalSocket);
+ return d->tcpSocket->isValid();
+}
+
+qint64 QLocalSocket::readBufferSize() const
+{
+ Q_D(const QLocalSocket);
+ return d->tcpSocket->readBufferSize();
+}
+
+void QLocalSocket::setReadBufferSize(qint64 size)
+{
+ Q_D(QLocalSocket);
+ d->tcpSocket->setReadBufferSize(size);
+}
+
+bool QLocalSocket::waitForConnected(int msec)
+{
+ Q_D(QLocalSocket);
+ if (state() != ConnectingState)
+ return (state() == ConnectedState);
+
+ return d->tcpSocket->waitForConnected(msec);
+}
+
+bool QLocalSocket::waitForDisconnected(int msecs)
+{
+ Q_D(QLocalSocket);
+ if (state() == UnconnectedState) {
+ qWarning() << "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState";
+ return false;
+ }
+ return (d->tcpSocket->waitForDisconnected(msecs));
+}
+
+bool QLocalSocket::waitForReadyRead(int msecs)
+{
+ Q_D(QLocalSocket);
+ if (state() == QLocalSocket::UnconnectedState)
+ return false;
+ return (d->tcpSocket->waitForReadyRead(msecs));
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp
new file mode 100644
index 0000000000..da85d917e4
--- /dev/null
+++ b/src/network/socket/qlocalsocket_unix.cpp
@@ -0,0 +1,581 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalsocket.h"
+#include "qlocalsocket_p.h"
+#include "qnet_unix_p.h"
+
+#ifndef QT_NO_LOCALSOCKET
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <qdir.h>
+#include <qdebug.h>
+#include <qelapsedtimer.h>
+
+#ifdef Q_OS_VXWORKS
+# include <selectLib.h>
+#endif
+
+#define QT_CONNECT_TIMEOUT 30000
+
+QT_BEGIN_NAMESPACE
+
+QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
+ delayConnect(0),
+ connectTimer(0),
+ connectingSocket(-1),
+ connectingOpenMode(0),
+ state(QLocalSocket::UnconnectedState)
+{
+}
+
+void QLocalSocketPrivate::init()
+{
+ Q_Q(QLocalSocket);
+ // QIODevice signals
+ q->connect(&unixSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
+ q->connect(&unixSocket, SIGNAL(bytesWritten(qint64)),
+ q, SIGNAL(bytesWritten(qint64)));
+ q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
+ // QAbstractSocket signals
+ q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
+ q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
+ q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
+ q->connect(&unixSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ q, SLOT(_q_error(QAbstractSocket::SocketError)));
+ q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
+ unixSocket.setParent(q);
+}
+
+void QLocalSocketPrivate::_q_error(QAbstractSocket::SocketError socketError)
+{
+ Q_Q(QLocalSocket);
+ QString function = QLatin1String("QLocalSocket");
+ QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
+ QString errorString = generateErrorString(error, function);
+ q->setErrorString(errorString);
+ emit q->error(error);
+}
+
+void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
+{
+ Q_Q(QLocalSocket);
+ QLocalSocket::LocalSocketState currentState = state;
+ switch(newState) {
+ case QAbstractSocket::UnconnectedState:
+ state = QLocalSocket::UnconnectedState;
+ serverName.clear();
+ fullServerName.clear();
+ break;
+ case QAbstractSocket::ConnectingState:
+ state = QLocalSocket::ConnectingState;
+ break;
+ case QAbstractSocket::ConnectedState:
+ state = QLocalSocket::ConnectedState;
+ break;
+ case QAbstractSocket::ClosingState:
+ state = QLocalSocket::ClosingState;
+ break;
+ default:
+#if defined QLOCALSOCKET_DEBUG
+ qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
+#endif
+ return;
+ }
+ if (currentState != state)
+ emit q->stateChanged(state);
+}
+
+QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
+{
+ QString errorString;
+ switch (error) {
+ case QLocalSocket::ConnectionRefusedError:
+ errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
+ break;
+ case QLocalSocket::PeerClosedError:
+ errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
+ break;
+ case QLocalSocket::ServerNotFoundError:
+ errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
+ break;
+ case QLocalSocket::SocketAccessError:
+ errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
+ break;
+ case QLocalSocket::SocketResourceError:
+ errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
+ break;
+ case QLocalSocket::SocketTimeoutError:
+ errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
+ break;
+ case QLocalSocket::DatagramTooLargeError:
+ errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
+ break;
+ case QLocalSocket::ConnectionError:
+ errorString = QLocalSocket::tr("%1: Connection error").arg(function);
+ break;
+ case QLocalSocket::UnsupportedSocketOperationError:
+ errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
+ break;
+ case QLocalSocket::UnknownSocketError:
+ default:
+ errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(errno);
+ }
+ return errorString;
+}
+
+void QLocalSocketPrivate::errorOccurred(QLocalSocket::LocalSocketError error, const QString &function)
+{
+ Q_Q(QLocalSocket);
+ switch (error) {
+ case QLocalSocket::ConnectionRefusedError:
+ unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
+ break;
+ case QLocalSocket::PeerClosedError:
+ unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
+ break;
+ case QLocalSocket::ServerNotFoundError:
+ unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
+ break;
+ case QLocalSocket::SocketAccessError:
+ unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
+ break;
+ case QLocalSocket::SocketResourceError:
+ unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
+ break;
+ case QLocalSocket::SocketTimeoutError:
+ unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
+ break;
+ case QLocalSocket::DatagramTooLargeError:
+ unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
+ break;
+ case QLocalSocket::ConnectionError:
+ unixSocket.setSocketError(QAbstractSocket::NetworkError);
+ break;
+ case QLocalSocket::UnsupportedSocketOperationError:
+ unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
+ break;
+ case QLocalSocket::UnknownSocketError:
+ default:
+ unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
+ }
+
+ QString errorString = generateErrorString(error, function);
+ q->setErrorString(errorString);
+ emit q->error(error);
+
+ // errors cause a disconnect
+ unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
+ bool stateChanged = (state != QLocalSocket::UnconnectedState);
+ state = QLocalSocket::UnconnectedState;
+ q->close();
+ if (stateChanged)
+ q->emit stateChanged(state);
+}
+
+void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ if (state() == ConnectedState
+ || state() == ConnectingState)
+ return;
+
+ d->errorString.clear();
+ d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
+ d->state = ConnectingState;
+ emit stateChanged(d->state);
+
+ if (name.isEmpty()) {
+ d->errorOccurred(ServerNotFoundError,
+ QLatin1String("QLocalSocket::connectToServer"));
+ return;
+ }
+
+ // create the socket
+ if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0))) {
+ d->errorOccurred(UnsupportedSocketOperationError,
+ QLatin1String("QLocalSocket::connectToServer"));
+ return;
+ }
+#ifndef Q_OS_SYMBIAN
+ // set non blocking so we can try to connect and it wont wait
+ int flags = fcntl(d->connectingSocket, F_GETFL, 0);
+ if (-1 == flags
+ || -1 == (fcntl(d->connectingSocket, F_SETFL, flags | O_NONBLOCK))) {
+ d->errorOccurred(UnknownSocketError,
+ QLatin1String("QLocalSocket::connectToServer"));
+ return;
+ }
+#endif
+
+ // _q_connectToSocket does the actual connecting
+ d->connectingName = name;
+ d->connectingOpenMode = openMode;
+ d->_q_connectToSocket();
+}
+
+/*!
+ \internal
+
+ Tries to connect connectingName and connectingOpenMode
+
+ \sa connectToServer() waitForConnected()
+ */
+void QLocalSocketPrivate::_q_connectToSocket()
+{
+ Q_Q(QLocalSocket);
+ QString connectingPathName;
+
+ // determine the full server path
+ if (connectingName.startsWith(QLatin1Char('/'))) {
+ connectingPathName = connectingName;
+ } else {
+ connectingPathName = QDir::tempPath();
+ connectingPathName += QLatin1Char('/') + connectingName;
+ }
+
+ struct sockaddr_un name;
+ name.sun_family = PF_UNIX;
+ if (sizeof(name.sun_path) < (uint)connectingPathName.toLatin1().size() + 1) {
+ QString function = QLatin1String("QLocalSocket::connectToServer");
+ errorOccurred(QLocalSocket::ServerNotFoundError, function);
+ return;
+ }
+ ::memcpy(name.sun_path, connectingPathName.toLatin1().data(),
+ connectingPathName.toLatin1().size() + 1);
+ if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) {
+ QString function = QLatin1String("QLocalSocket::connectToServer");
+ switch (errno)
+ {
+ case EINVAL:
+ case ECONNREFUSED:
+ errorOccurred(QLocalSocket::ConnectionRefusedError, function);
+ break;
+ case ENOENT:
+ errorOccurred(QLocalSocket::ServerNotFoundError, function);
+ break;
+ case EACCES:
+ case EPERM:
+ errorOccurred(QLocalSocket::SocketAccessError, function);
+ break;
+ case ETIMEDOUT:
+ errorOccurred(QLocalSocket::SocketTimeoutError, function);
+ break;
+ case EAGAIN:
+ // Try again later, all of the sockets listening are full
+ if (!delayConnect) {
+ delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
+ q->connect(delayConnect, SIGNAL(activated(int)), q, SLOT(_q_connectToSocket()));
+ }
+ if (!connectTimer) {
+ connectTimer = new QTimer(q);
+ q->connect(connectTimer, SIGNAL(timeout()),
+ q, SLOT(_q_abortConnectionAttempt()),
+ Qt::DirectConnection);
+ connectTimer->start(QT_CONNECT_TIMEOUT);
+ }
+ delayConnect->setEnabled(true);
+ break;
+ default:
+ errorOccurred(QLocalSocket::UnknownSocketError, function);
+ }
+ return;
+ }
+
+ // connected!
+ cancelDelayedConnect();
+
+ serverName = connectingName;
+ fullServerName = connectingPathName;
+ if (unixSocket.setSocketDescriptor(connectingSocket,
+ QAbstractSocket::ConnectedState, connectingOpenMode)) {
+ q->QIODevice::open(connectingOpenMode);
+ q->emit connected();
+ } else {
+ QString function = QLatin1String("QLocalSocket::connectToServer");
+ errorOccurred(QLocalSocket::UnknownSocketError, function);
+ }
+ connectingSocket = -1;
+ connectingName.clear();
+ connectingOpenMode = 0;
+}
+
+bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
+ LocalSocketState socketState, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
+ switch (socketState) {
+ case ConnectingState:
+ newSocketState = QAbstractSocket::ConnectingState;
+ break;
+ case ConnectedState:
+ newSocketState = QAbstractSocket::ConnectedState;
+ break;
+ case ClosingState:
+ newSocketState = QAbstractSocket::ClosingState;
+ break;
+ case UnconnectedState:
+ newSocketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+ QIODevice::open(openMode);
+ d->state = socketState;
+ return d->unixSocket.setSocketDescriptor(socketDescriptor,
+ newSocketState, openMode);
+}
+
+void QLocalSocketPrivate::_q_abortConnectionAttempt()
+{
+ Q_Q(QLocalSocket);
+ q->close();
+}
+
+void QLocalSocketPrivate::cancelDelayedConnect()
+{
+ if (delayConnect) {
+ delayConnect->setEnabled(false);
+ delete delayConnect;
+ delayConnect = 0;
+ connectTimer->stop();
+ delete connectTimer;
+ connectTimer = 0;
+ }
+}
+
+quintptr QLocalSocket::socketDescriptor() const
+{
+ Q_D(const QLocalSocket);
+ return d->unixSocket.socketDescriptor();
+}
+
+qint64 QLocalSocket::readData(char *data, qint64 c)
+{
+ Q_D(QLocalSocket);
+ return d->unixSocket.readData(data, c);
+}
+
+qint64 QLocalSocket::writeData(const char *data, qint64 c)
+{
+ Q_D(QLocalSocket);
+ return d->unixSocket.writeData(data, c);
+}
+
+void QLocalSocket::abort()
+{
+ Q_D(QLocalSocket);
+ d->unixSocket.abort();
+}
+
+qint64 QLocalSocket::bytesAvailable() const
+{
+ Q_D(const QLocalSocket);
+ return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
+}
+
+qint64 QLocalSocket::bytesToWrite() const
+{
+ Q_D(const QLocalSocket);
+ return d->unixSocket.bytesToWrite();
+}
+
+bool QLocalSocket::canReadLine() const
+{
+ Q_D(const QLocalSocket);
+ return QIODevice::canReadLine() || d->unixSocket.canReadLine();
+}
+
+void QLocalSocket::close()
+{
+ Q_D(QLocalSocket);
+ d->unixSocket.close();
+ d->cancelDelayedConnect();
+ if (d->connectingSocket != -1)
+ ::close(d->connectingSocket);
+ d->connectingSocket = -1;
+ d->connectingName.clear();
+ d->connectingOpenMode = 0;
+ d->serverName.clear();
+ d->fullServerName.clear();
+ QIODevice::close();
+}
+
+bool QLocalSocket::waitForBytesWritten(int msecs)
+{
+ Q_D(QLocalSocket);
+ return d->unixSocket.waitForBytesWritten(msecs);
+}
+
+bool QLocalSocket::flush()
+{
+ Q_D(QLocalSocket);
+ return d->unixSocket.flush();
+}
+
+void QLocalSocket::disconnectFromServer()
+{
+ Q_D(QLocalSocket);
+ d->unixSocket.disconnectFromHost();
+}
+
+QLocalSocket::LocalSocketError QLocalSocket::error() const
+{
+ Q_D(const QLocalSocket);
+ switch (d->unixSocket.error()) {
+ case QAbstractSocket::ConnectionRefusedError:
+ return QLocalSocket::ConnectionRefusedError;
+ case QAbstractSocket::RemoteHostClosedError:
+ return QLocalSocket::PeerClosedError;
+ case QAbstractSocket::HostNotFoundError:
+ return QLocalSocket::ServerNotFoundError;
+ case QAbstractSocket::SocketAccessError:
+ return QLocalSocket::SocketAccessError;
+ case QAbstractSocket::SocketResourceError:
+ return QLocalSocket::SocketResourceError;
+ case QAbstractSocket::SocketTimeoutError:
+ return QLocalSocket::SocketTimeoutError;
+ case QAbstractSocket::DatagramTooLargeError:
+ return QLocalSocket::DatagramTooLargeError;
+ case QAbstractSocket::NetworkError:
+ return QLocalSocket::ConnectionError;
+ case QAbstractSocket::UnsupportedSocketOperationError:
+ return QLocalSocket::UnsupportedSocketOperationError;
+ case QAbstractSocket::UnknownSocketError:
+ return QLocalSocket::UnknownSocketError;
+ default:
+#if defined QLOCALSOCKET_DEBUG
+ qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
+#endif
+ break;
+ }
+ return UnknownSocketError;
+}
+
+bool QLocalSocket::isValid() const
+{
+ Q_D(const QLocalSocket);
+ return d->unixSocket.isValid();
+}
+
+qint64 QLocalSocket::readBufferSize() const
+{
+ Q_D(const QLocalSocket);
+ return d->unixSocket.readBufferSize();
+}
+
+void QLocalSocket::setReadBufferSize(qint64 size)
+{
+ Q_D(QLocalSocket);
+ d->unixSocket.setReadBufferSize(size);
+}
+
+bool QLocalSocket::waitForConnected(int msec)
+{
+ Q_D(QLocalSocket);
+ if (state() != ConnectingState)
+ return (state() == ConnectedState);
+
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(d->connectingSocket, &fds);
+
+ timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+
+ // timeout can not be 0 or else select will return an error.
+ if (0 == msec)
+ timeout.tv_usec = 1000;
+
+ int result = -1;
+ // on Linux timeout will be updated by select, but _not_ on other systems.
+ QElapsedTimer timer;
+ timer.start();
+ while (state() == ConnectingState
+ && (-1 == msec || timer.elapsed() < msec)) {
+#ifdef Q_OS_SYMBIAN
+ // On Symbian, ready-to-write is signaled when non-blocking socket
+ // connect is finised. Is ready-to-read really used on other
+ // UNIX paltforms when using non-blocking AF_UNIX socket?
+ result = ::select(d->connectingSocket + 1, 0, &fds, 0, &timeout);
+#else
+ result = ::select(d->connectingSocket + 1, &fds, 0, 0, &timeout);
+#endif
+ if (-1 == result && errno != EINTR) {
+ d->errorOccurred( QLocalSocket::UnknownSocketError,
+ QLatin1String("QLocalSocket::waitForConnected"));
+ break;
+ }
+ if (result > 0)
+ d->_q_connectToSocket();
+ }
+
+ return (state() == ConnectedState);
+}
+
+bool QLocalSocket::waitForDisconnected(int msecs)
+{
+ Q_D(QLocalSocket);
+ if (state() == UnconnectedState) {
+ qWarning() << "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState";
+ return false;
+ }
+ return (d->unixSocket.waitForDisconnected(msecs));
+}
+
+bool QLocalSocket::waitForReadyRead(int msecs)
+{
+ Q_D(QLocalSocket);
+ if (state() == QLocalSocket::UnconnectedState)
+ return false;
+ return (d->unixSocket.waitForReadyRead(msecs));
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/socket/qlocalsocket_win.cpp b/src/network/socket/qlocalsocket_win.cpp
new file mode 100644
index 0000000000..185f57481a
--- /dev/null
+++ b/src/network/socket/qlocalsocket_win.cpp
@@ -0,0 +1,633 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlocalsocket_p.h"
+
+#include <private/qthread_p.h>
+#include <qcoreapplication.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+void QLocalSocketPrivate::init()
+{
+ Q_Q(QLocalSocket);
+ memset(&overlapped, 0, sizeof(overlapped));
+ overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ dataReadNotifier = new QWinEventNotifier(overlapped.hEvent, q);
+ q->connect(dataReadNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_notified()));
+}
+
+void QLocalSocketPrivate::setErrorString(const QString &function)
+{
+ Q_Q(QLocalSocket);
+ BOOL windowsError = GetLastError();
+ QLocalSocket::LocalSocketState currentState = state;
+
+ // If the connectToServer fails due to WaitNamedPipe() time-out, assume ConnectionError
+ if (state == QLocalSocket::ConnectingState && windowsError == ERROR_SEM_TIMEOUT)
+ windowsError = ERROR_NO_DATA;
+
+ switch (windowsError) {
+ case ERROR_PIPE_NOT_CONNECTED:
+ case ERROR_BROKEN_PIPE:
+ case ERROR_NO_DATA:
+ error = QLocalSocket::ConnectionError;
+ errorString = QLocalSocket::tr("%1: Connection error").arg(function);
+ state = QLocalSocket::UnconnectedState;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ error = QLocalSocket::ServerNotFoundError;
+ 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);
+#if defined QLOCALSOCKET_DEBUG
+ qWarning() << "QLocalSocket error not handled:" << errorString;
+#endif
+ state = QLocalSocket::UnconnectedState;
+ }
+
+ if (currentState != state) {
+ q->emit stateChanged(state);
+ if (state == QLocalSocket::UnconnectedState)
+ q->emit disconnected();
+ }
+ emit q->error(error);
+}
+
+QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
+ handle(INVALID_HANDLE_VALUE),
+ pipeWriter(0),
+ readBufferMaxSize(0),
+ actualReadBufferSize(0),
+ error(QLocalSocket::UnknownSocketError),
+ readSequenceStarted(false),
+ pendingReadyRead(false),
+ pipeClosed(false),
+ state(QLocalSocket::UnconnectedState)
+{
+}
+
+QLocalSocketPrivate::~QLocalSocketPrivate()
+{
+ destroyPipeHandles();
+ CloseHandle(overlapped.hEvent);
+}
+
+void QLocalSocketPrivate::destroyPipeHandles()
+{
+ if (handle != INVALID_HANDLE_VALUE) {
+ DisconnectNamedPipe(handle);
+ CloseHandle(handle);
+ }
+}
+
+void QLocalSocket::connectToServer(const QString &name, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ if (state() == ConnectedState || state() == ConnectingState)
+ return;
+
+ d->error = QLocalSocket::UnknownSocketError;
+ d->errorString = QString();
+ d->state = ConnectingState;
+ emit stateChanged(d->state);
+ if (name.isEmpty()) {
+ d->error = QLocalSocket::ServerNotFoundError;
+ setErrorString(QLocalSocket::tr("%1: Invalid name").arg(QLatin1String("QLocalSocket::connectToServer")));
+ d->state = UnconnectedState;
+ emit error(d->error);
+ emit stateChanged(d->state);
+ return;
+ }
+
+ QString pipePath = QLatin1String("\\\\.\\pipe\\");
+ if (name.startsWith(pipePath))
+ d->fullServerName = name;
+ else
+ d->fullServerName = pipePath + name;
+ // Try to open a named pipe
+ HANDLE localSocket;
+ forever {
+ DWORD permissions = (openMode & QIODevice::ReadOnly) ? GENERIC_READ : 0;
+ permissions |= (openMode & QIODevice::WriteOnly) ? GENERIC_WRITE : 0;
+ localSocket = CreateFile((const wchar_t *)d->fullServerName.utf16(), // pipe name
+ permissions,
+ 0, // no sharing
+ NULL, // default security attributes
+ OPEN_EXISTING, // opens existing pipe
+ FILE_FLAG_OVERLAPPED,
+ NULL); // no template file
+
+ if (localSocket != INVALID_HANDLE_VALUE)
+ break;
+ DWORD error = GetLastError();
+ // It is really an error only if it is not ERROR_PIPE_BUSY
+ if (ERROR_PIPE_BUSY != error) {
+ break;
+ }
+
+ // All pipe instances are busy, so wait until connected or up to 5 seconds.
+ if (!WaitNamedPipe((const wchar_t *)d->fullServerName.utf16(), 5000))
+ break;
+ }
+
+ if (localSocket == INVALID_HANDLE_VALUE) {
+ d->setErrorString(QLatin1String("QLocalSocket::connectToServer"));
+ d->fullServerName = QString();
+ return;
+ }
+
+ // we have a valid handle
+ d->serverName = name;
+ if (setSocketDescriptor((quintptr)localSocket, ConnectedState, openMode)) {
+ d->handle = localSocket;
+ emit connected();
+ }
+}
+
+// This is reading from the buffer
+qint64 QLocalSocket::readData(char *data, qint64 maxSize)
+{
+ Q_D(QLocalSocket);
+
+ if (d->pipeClosed && d->actualReadBufferSize == 0)
+ return -1; // signal EOF
+
+ qint64 readSoFar;
+ // If startAsyncRead() read data, copy it to its destination.
+ if (maxSize == 1 && d->actualReadBufferSize > 0) {
+ *data = d->readBuffer.getChar();
+ d->actualReadBufferSize--;
+ readSoFar = 1;
+ } else {
+ qint64 bytesToRead = qMin(qint64(d->actualReadBufferSize), maxSize);
+ readSoFar = 0;
+ while (readSoFar < bytesToRead) {
+ const char *ptr = d->readBuffer.readPointer();
+ int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar,
+ qint64(d->readBuffer.nextDataBlockSize()));
+ memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
+ readSoFar += bytesToReadFromThisBlock;
+ d->readBuffer.free(bytesToReadFromThisBlock);
+ d->actualReadBufferSize -= bytesToReadFromThisBlock;
+ }
+ }
+
+ if (d->pipeClosed) {
+ if (d->actualReadBufferSize == 0)
+ QTimer::singleShot(0, this, SLOT(_q_pipeClosed()));
+ } else {
+ if (!d->readSequenceStarted)
+ d->startAsyncRead();
+ d->checkReadyRead();
+ }
+
+ return readSoFar;
+}
+
+/*!
+ \internal
+ Schedules or cancels a readyRead() emission depending on actual data availability
+ */
+void QLocalSocketPrivate::checkReadyRead()
+{
+ if (actualReadBufferSize > 0) {
+ if (!pendingReadyRead) {
+ Q_Q(QLocalSocket);
+ QTimer::singleShot(0, q, SLOT(_q_emitReadyRead()));
+ pendingReadyRead = true;
+ }
+ } else {
+ pendingReadyRead = false;
+ }
+}
+
+/*!
+ \internal
+ Reads data from the socket into the readbuffer
+ */
+void QLocalSocketPrivate::startAsyncRead()
+{
+ do {
+ DWORD bytesToRead = checkPipeState();
+ if (pipeClosed)
+ return;
+
+ if (bytesToRead == 0) {
+ // There are no bytes in the pipe but we need to
+ // start the overlapped read with some buffer size.
+ bytesToRead = initialReadBufferSize;
+ }
+
+ if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
+ bytesToRead = readBufferMaxSize - readBuffer.size();
+ if (bytesToRead == 0) {
+ // Buffer is full. User must read data from the buffer
+ // before we can read more from the pipe.
+ return;
+ }
+ }
+
+ char *ptr = readBuffer.reserve(bytesToRead);
+
+ readSequenceStarted = true;
+ if (ReadFile(handle, ptr, bytesToRead, NULL, &overlapped)) {
+ completeAsyncRead();
+ } else {
+ switch (GetLastError()) {
+ case ERROR_IO_PENDING:
+ // This is not an error. We're getting notified, when data arrives.
+ return;
+ case ERROR_MORE_DATA:
+ // This is not an error. The synchronous read succeeded.
+ // We're connected to a message mode pipe and the message
+ // didn't fit into the pipe's system buffer.
+ completeAsyncRead();
+ break;
+ case ERROR_PIPE_NOT_CONNECTED:
+ {
+ // It may happen, that the other side closes the connection directly
+ // after writing data. Then we must set the appropriate socket state.
+ pipeClosed = true;
+ Q_Q(QLocalSocket);
+ emit q->readChannelFinished();
+ return;
+ }
+ default:
+ setErrorString(QLatin1String("QLocalSocketPrivate::startAsyncRead"));
+ return;
+ }
+ }
+ } while (!readSequenceStarted);
+}
+
+/*!
+ \internal
+ Sets the correct size of the read buffer after a read operation.
+ Returns false, if an error occurred or the connection dropped.
+ */
+bool QLocalSocketPrivate::completeAsyncRead()
+{
+ ResetEvent(overlapped.hEvent);
+ readSequenceStarted = false;
+
+ DWORD bytesRead;
+ if (!GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE)) {
+ switch (GetLastError()) {
+ case ERROR_MORE_DATA:
+ // This is not an error. We're connected to a message mode
+ // pipe and the message didn't fit into the pipe's system
+ // buffer. We will read the remaining data in the next call.
+ break;
+ case ERROR_PIPE_NOT_CONNECTED:
+ return false;
+ default:
+ setErrorString(QLatin1String("QLocalSocketPrivate::completeAsyncRead"));
+ return false;
+ }
+ }
+
+ actualReadBufferSize += bytesRead;
+ readBuffer.truncate(actualReadBufferSize);
+ return true;
+}
+
+qint64 QLocalSocket::writeData(const char *data, qint64 maxSize)
+{
+ Q_D(QLocalSocket);
+ if (!d->pipeWriter) {
+ d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
+ connect(d->pipeWriter, SIGNAL(canWrite()), this, SLOT(_q_canWrite()));
+ connect(d->pipeWriter, SIGNAL(bytesWritten(qint64)), this, SIGNAL(bytesWritten(qint64)));
+ d->pipeWriter->start();
+ }
+ return d->pipeWriter->write(data, maxSize);
+}
+
+void QLocalSocket::abort()
+{
+ Q_D(QLocalSocket);
+ if (d->pipeWriter) {
+ delete d->pipeWriter;
+ d->pipeWriter = 0;
+ }
+ close();
+}
+
+/*!
+ \internal
+ Returns the number of available bytes in the pipe.
+ Sets QLocalSocketPrivate::pipeClosed to true if the connection is broken.
+ */
+DWORD QLocalSocketPrivate::checkPipeState()
+{
+ Q_Q(QLocalSocket);
+ DWORD bytes;
+ if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) {
+ return bytes;
+ } else {
+ if (!pipeClosed) {
+ pipeClosed = true;
+ emit q->readChannelFinished();
+ if (actualReadBufferSize == 0)
+ QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
+ }
+ }
+ return 0;
+}
+
+void QLocalSocketPrivate::_q_pipeClosed()
+{
+ Q_Q(QLocalSocket);
+ q->close();
+}
+
+qint64 QLocalSocket::bytesAvailable() const
+{
+ Q_D(const QLocalSocket);
+ qint64 available = QIODevice::bytesAvailable();
+ available += (qint64) d->actualReadBufferSize;
+ return available;
+}
+
+qint64 QLocalSocket::bytesToWrite() const
+{
+ Q_D(const QLocalSocket);
+ return (d->pipeWriter) ? d->pipeWriter->bytesToWrite() : 0;
+}
+
+bool QLocalSocket::canReadLine() const
+{
+ Q_D(const QLocalSocket);
+ if (state() != ConnectedState)
+ return false;
+ return (QIODevice::canReadLine()
+ || d->readBuffer.indexOf('\n', d->actualReadBufferSize) != -1);
+}
+
+void QLocalSocket::close()
+{
+ Q_D(QLocalSocket);
+ if (state() == UnconnectedState)
+ return;
+
+ QIODevice::close();
+ d->state = ClosingState;
+ emit stateChanged(d->state);
+ if (!d->pipeClosed)
+ emit readChannelFinished();
+ d->serverName = QString();
+ d->fullServerName = QString();
+
+ if (state() != UnconnectedState && bytesToWrite() > 0) {
+ disconnectFromServer();
+ return;
+ }
+ d->readSequenceStarted = false;
+ d->pendingReadyRead = false;
+ d->pipeClosed = false;
+ d->destroyPipeHandles();
+ d->handle = INVALID_HANDLE_VALUE;
+ ResetEvent(d->overlapped.hEvent);
+ d->state = UnconnectedState;
+ emit stateChanged(d->state);
+ emit disconnected();
+ if (d->pipeWriter) {
+ delete d->pipeWriter;
+ d->pipeWriter = 0;
+ }
+}
+
+bool QLocalSocket::flush()
+{
+ Q_D(QLocalSocket);
+ if (d->pipeWriter)
+ return d->pipeWriter->waitForWrite(0);
+ return false;
+}
+
+void QLocalSocket::disconnectFromServer()
+{
+ Q_D(QLocalSocket);
+
+ // Are we still connected?
+ if (!isValid()) {
+ // If we have unwritten data, the pipeWriter is still present.
+ // It must be destroyed before close() to prevent an infinite loop.
+ delete d->pipeWriter;
+ d->pipeWriter = 0;
+ }
+
+ flush();
+ if (d->pipeWriter && d->pipeWriter->bytesToWrite() != 0) {
+ d->state = QLocalSocket::ClosingState;
+ emit stateChanged(d->state);
+ } else {
+ close();
+ }
+}
+
+QLocalSocket::LocalSocketError QLocalSocket::error() const
+{
+ Q_D(const QLocalSocket);
+ return d->error;
+}
+
+bool QLocalSocket::setSocketDescriptor(quintptr socketDescriptor,
+ LocalSocketState socketState, OpenMode openMode)
+{
+ Q_D(QLocalSocket);
+ d->readBuffer.clear();
+ d->actualReadBufferSize = 0;
+ QIODevice::open(openMode);
+ d->handle = (int*)socketDescriptor;
+ d->state = socketState;
+ emit stateChanged(d->state);
+ if (d->state == ConnectedState && openMode.testFlag(QIODevice::ReadOnly)) {
+ d->startAsyncRead();
+ d->checkReadyRead();
+ }
+ return true;
+}
+
+void QLocalSocketPrivate::_q_canWrite()
+{
+ Q_Q(QLocalSocket);
+ if (state == QLocalSocket::ClosingState)
+ q->close();
+}
+
+void QLocalSocketPrivate::_q_notified()
+{
+ Q_Q(QLocalSocket);
+ if (!completeAsyncRead()) {
+ pipeClosed = true;
+ emit q->readChannelFinished();
+ if (actualReadBufferSize == 0)
+ QTimer::singleShot(0, q, SLOT(_q_pipeClosed()));
+ return;
+ }
+ startAsyncRead();
+ pendingReadyRead = false;
+ emit q->readyRead();
+}
+
+void QLocalSocketPrivate::_q_emitReadyRead()
+{
+ if (pendingReadyRead) {
+ Q_Q(QLocalSocket);
+ pendingReadyRead = false;
+ emit q->readyRead();
+ }
+}
+
+quintptr QLocalSocket::socketDescriptor() const
+{
+ Q_D(const QLocalSocket);
+ return (quintptr)d->handle;
+}
+
+qint64 QLocalSocket::readBufferSize() const
+{
+ Q_D(const QLocalSocket);
+ return d->readBufferMaxSize;
+}
+
+void QLocalSocket::setReadBufferSize(qint64 size)
+{
+ Q_D(QLocalSocket);
+ d->readBufferMaxSize = size;
+}
+
+bool QLocalSocket::waitForConnected(int msecs)
+{
+ Q_UNUSED(msecs);
+ return (state() == ConnectedState);
+}
+
+bool QLocalSocket::waitForDisconnected(int msecs)
+{
+ Q_D(QLocalSocket);
+ if (state() == UnconnectedState)
+ return false;
+ if (!openMode().testFlag(QIODevice::ReadOnly)) {
+ qWarning("QLocalSocket::waitForDisconnected isn't supported for write only pipes.");
+ return false;
+ }
+ QIncrementalSleepTimer timer(msecs);
+ forever {
+ d->checkPipeState();
+ if (d->pipeClosed)
+ close();
+ if (state() == UnconnectedState)
+ return true;
+ Sleep(timer.nextSleepTime());
+ if (timer.hasTimedOut())
+ break;
+ }
+
+ return false;
+}
+
+bool QLocalSocket::isValid() const
+{
+ Q_D(const QLocalSocket);
+ return d->handle != INVALID_HANDLE_VALUE;
+}
+
+bool QLocalSocket::waitForReadyRead(int msecs)
+{
+ Q_D(QLocalSocket);
+
+ if (bytesAvailable() > 0)
+ return true;
+
+ if (d->state != QLocalSocket::ConnectedState)
+ return false;
+
+ // We already know that the pipe is gone, but did not enter the event loop yet.
+ if (d->pipeClosed) {
+ close();
+ return false;
+ }
+
+ Q_ASSERT(d->readSequenceStarted);
+ DWORD result = WaitForSingleObject(d->overlapped.hEvent, msecs == -1 ? INFINITE : msecs);
+ switch (result) {
+ case WAIT_OBJECT_0:
+ d->_q_notified();
+ // We just noticed that the pipe is gone.
+ if (d->pipeClosed) {
+ close();
+ return false;
+ }
+ return true;
+ case WAIT_TIMEOUT:
+ return false;
+ }
+
+ qWarning("QLocalSocket::waitForReadyRead WaitForSingleObject failed with error code %d.", int(GetLastError()));
+ return false;
+}
+
+bool QLocalSocket::waitForBytesWritten(int msecs)
+{
+ Q_D(const QLocalSocket);
+ if (!d->pipeWriter)
+ return false;
+
+ // Wait for the pipe writer to acknowledge that it has
+ // written. This will succeed if either the pipe writer has
+ // already written the data, or if it manages to write data
+ // within the given timeout.
+ return d->pipeWriter->waitForWrite(msecs);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
new file mode 100644
index 0000000000..f5a88e2cc7
--- /dev/null
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -0,0 +1,1258 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNATIVESOCKETENGINE_DEBUG
+
+/*! \class QNativeSocketEngine
+ \internal
+
+ \brief The QNativeSocketEngine class provides low level access to a socket.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ QtSocketLayer provides basic socket functionality provided by the
+ operating system. It also keeps track of what state the socket is
+ in, and which errors that occur.
+
+ The classes QTcpSocket, QUdpSocket and QTcpServer provide a
+ higher level API, and are in general more useful for the common
+ application.
+
+ There are two main ways of initializing the a QNativeSocketEngine; either
+ create a new socket by passing the socket type (TcpSocket or
+ UdpSocket) and network layer protocol (IPv4Protocol or
+ IPv6Protocol) to initialize(), or pass an existing socket
+ descriptor and have QNativeSocketEngine determine the type and protocol
+ itself. The native socket descriptor can later be fetched by
+ calling socketDescriptor(). The socket is made non-blocking, but
+ blocking behavior can still be achieved by calling waitForRead()
+ and waitForWrite(). isValid() can be called to check if the socket
+ has been successfully initialized and is ready to use.
+
+ To connect to a host, determine its address and pass this and the
+ port number to connectToHost(). The socket can then be used as a
+ TCP or UDP client. Otherwise; bind(), listen() and accept() are
+ used to have the socket function as a TCP or UDP server. Call
+ close() to close the socket.
+
+ bytesAvailable() is called to determine how much data is available
+ for reading. read() and write() are used by both TCP and UDP
+ clients to exchange data with the connected peer. UDP clients can
+ also call hasMoreDatagrams(), nextDatagramSize(),
+ readDatagram(), and writeDatagram().
+
+ Call state() to determine the state of the socket, for
+ example, ListeningState or ConnectedState. socketType() tells
+ whether the socket is a TCP socket or a UDP socket, or if the
+ socket type is unknown. protocol() is used to determine the
+ socket's network layer protocol.
+
+ localAddress(), localPort() are called to find the address and
+ port that are currently bound to the socket. If the socket is
+ connected, peerAddress() and peerPort() determine the address and
+ port of the connected peer.
+
+ Finally, if any function should fail, error() and
+ errorString() can be called to determine the cause of the error.
+*/
+
+#include <qabstracteventdispatcher.h>
+#include <qsocketnotifier.h>
+#include <qnetworkinterface.h>
+
+#include "qnativesocketengine_p.h"
+#include <private/qthread_p.h>
+#include <private/qobject_p.h>
+
+#if !defined(QT_NO_NETWORKPROXY)
+# include "qnetworkproxy.h"
+# include "qabstractsocket.h"
+# include "qtcpserver.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define QNATIVESOCKETENGINE_DEBUG
+
+#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)
+#define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a)
+
+/*! \internal
+ Constructs the private class and initializes all data members.
+
+ On Windows, WSAStartup is called "recursively" for every
+ concurrent QNativeSocketEngine. This is safe, because WSAStartup and
+ WSACleanup are reference counted.
+*/
+QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() :
+ socketDescriptor(-1),
+ readNotifier(0),
+ writeNotifier(0),
+ exceptNotifier(0)
+{
+}
+
+/*! \internal
+ Destructs the private class.
+*/
+QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate()
+{
+}
+
+/*! \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 QNativeSocketEnginePrivate::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 = QNativeSocketEngine::tr("Unable to initialize non-blocking socket");
+ break;
+ case BroadcastingInitFailedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to initialize broadcast socket");
+ break;
+ case NoIpV6ErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Attempt to use IPv6 socket on a platform with no IPv6 support");
+ break;
+ case RemoteHostClosedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The remote host closed the connection");
+ break;
+ case TimeOutErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Network operation timed out");
+ break;
+ case ResourceErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Out of resources");
+ break;
+ case OperationUnsupportedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unsupported socket operation");
+ break;
+ case ProtocolUnsupportedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Protocol type not supported");
+ break;
+ case InvalidSocketErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Invalid socket descriptor");
+ break;
+ case HostUnreachableErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Host unreachable");
+ break;
+ case NetworkUnreachableErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Network unreachable");
+ break;
+ case AccessErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Permission denied");
+ break;
+ case ConnectionTimeOutErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Connection timed out");
+ break;
+ case ConnectionRefusedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Connection refused");
+ break;
+ case AddressInuseErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The bound address is already in use");
+ break;
+ case AddressNotAvailableErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The address is not available");
+ break;
+ case AddressProtectedErrorString:
+ socketErrorString = QNativeSocketEngine::tr("The address is protected");
+ break;
+ case DatagramTooLargeErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Datagram was too large to send");
+ break;
+ case SendDatagramErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to send a message");
+ break;
+ case ReceiveDatagramErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to receive a message");
+ break;
+ case WriteErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unable to write");
+ break;
+ case ReadErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Network error");
+ break;
+ case PortInuseErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Another socket is already listening on the same port");
+ break;
+ case NotSocketErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Operation on non-socket");
+ break;
+ case InvalidProxyTypeString:
+ socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation");
+ break;
+ case UnknownSocketErrorString:
+ socketErrorString = QNativeSocketEngine::tr("Unknown error");
+ break;
+ }
+}
+
+bool QNativeSocketEnginePrivate::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) {
+ // QNativeSocketEngine doesn't do proxies
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::InvalidProxyTypeString);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/*!
+ Constructs a QNativeSocketEngine.
+
+ \sa initialize()
+*/
+QNativeSocketEngine::QNativeSocketEngine(QObject *parent)
+ : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent)
+{
+}
+
+/*!
+ Destructs a QNativeSocketEngine.
+*/
+QNativeSocketEngine::~QNativeSocketEngine()
+{
+ close();
+}
+
+/*!
+ Initializes a QNativeSocketEngine 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 QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol protocol)
+{
+ Q_D(QNativeSocketEngine);
+ if (isValid())
+ close();
+
+#if defined(QT_NO_IPV6)
+ if (protocol == QAbstractSocket::IPv6Protocol) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::NoIpV6ErrorString);
+ return false;
+ }
+#endif
+
+ // 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("QNativeSocketEngine::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,
+ QNativeSocketEnginePrivate::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,
+ QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString);
+ close();
+ return false;
+ }
+
+
+ // Make sure we receive out-of-band data
+ if (socketType == QAbstractSocket::TcpSocket
+ && !setOption(ReceiveOutOfBandData, 1)) {
+ qWarning("QNativeSocketEngine::initialize unable to inline out-of-band data");
+ }
+
+ // Before Qt 4.6, we always set the send and receive buffer size to 49152 as
+ // this was found to be an optimal value. However, modern OS
+ // all have some kind of auto tuning for this and we therefore don't set
+ // this explictly anymore.
+ // If it introduces any performance regressions for Qt 4.6.x (x > 0) then
+ // it will be put back in.
+ //
+ // You can use tests/manual/qhttpnetworkconnection to test HTTP download speed
+ // with this.
+ //
+ // pre-4.6:
+ // setReceiveBufferSize(49152);
+ // setSendBufferSize(49152);
+
+ 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 QNativeSocketEngine::initialize(int socketDescriptor, QAbstractSocket::SocketState socketState)
+{
+ Q_D(QNativeSocketEngine);
+
+ if (isValid())
+ close();
+
+ d->socketDescriptor = socketDescriptor;
+
+ // determine socket type and protocol
+ if (!d->fetchConnectionParameters()) {
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEngine::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,
+ QNativeSocketEnginePrivate::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,
+ QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString);
+ close();
+ return false;
+ }
+ }
+
+ 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 QNativeSocketEngine::isValid() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->socketDescriptor != -1;
+}
+
+/*!
+ Returns the native socket descriptor. Any use of this descriptor
+ stands the risk of being non-portable.
+*/
+int QNativeSocketEngine::socketDescriptor() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->socketDescriptor;
+}
+
+/*!
+ Connects to the IP address and port specified by \a address and \a
+ port. If the connection is established, this function returns true
+ and the socket enters ConnectedState. Otherwise, false is
+ returned.
+
+ If false is returned, state() should be called to see if the
+ socket is in ConnectingState. If so, a delayed TCP connection is
+ taking place, and connectToHost() must be called again later to
+ determine if the connection was established successfully or
+ not. The second connection attempt must be made when the socket is
+ ready for writing. This state can be determined either by
+ connecting a QSocketNotifier to the socket descriptor returned by
+ socketDescriptor(), or by calling the blocking function
+ waitForWrite().
+
+ Example:
+ \snippet doc/src/snippets/code/src_network_socket_qnativesocketengine.cpp 0
+
+ Otherwise, error() should be called to determine the cause of the
+ error.
+*/
+bool QNativeSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::connectToHost(), false);
+
+#if defined (QT_NO_IPV6)
+ if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::NoIpV6ErrorString);
+ return false;
+ }
+#endif
+ if (!d->checkProxy(address))
+ return false;
+
+ Q_CHECK_STATES(QNativeSocketEngine::connectToHost(),
+ QAbstractSocket::UnconnectedState, QAbstractSocket::ConnectingState, false);
+
+ d->peerAddress = address;
+ d->peerPort = port;
+ bool connected = d->nativeConnect(address, port);
+ if (connected)
+ d->fetchConnectionParameters();
+
+ return connected;
+}
+
+/*!
+ If there's a connection activity on the socket, process it. Then
+ notify our parent if there really was activity.
+*/
+void QNativeSocketEngine::connectionNotification()
+{
+ Q_D(QNativeSocketEngine);
+ Q_ASSERT(state() == QAbstractSocket::ConnectingState);
+
+ connectToHost(d->peerAddress, d->peerPort);
+ if (state() != QAbstractSocket::ConnectingState) {
+ // we changed states
+ QAbstractSocketEngine::connectionNotification();
+ }
+}
+
+/*!
+ 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 QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port)
+{
+ Q_UNUSED(name);
+ Q_UNUSED(port);
+ Q_D(QNativeSocketEngine);
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::OperationUnsupportedErrorString);
+ return false;
+}
+
+/*!
+ Binds the socket to the address \a address and port \a
+ port. Returns true on success; otherwise false is returned. The
+ port may be 0, in which case an arbitrary unused port is assigned
+ automatically by the operating system.
+
+ Servers call this function to set up the server's address and
+ port. TCP servers must in addition call listen() after bind().
+*/
+bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bind(), false);
+
+#if defined (QT_NO_IPV6)
+ if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ d->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QNativeSocketEnginePrivate::NoIpV6ErrorString);
+ return false;
+ }
+#endif
+ if (!d->checkProxy(address))
+ return false;
+
+ Q_CHECK_STATE(QNativeSocketEngine::bind(), QAbstractSocket::UnconnectedState, false);
+
+ if (!d->nativeBind(address, port))
+ return false;
+
+ d->fetchConnectionParameters();
+ return true;
+}
+
+/*!
+ Prepares a TCP server for accepting incoming connections. This
+ function must be called after bind(), and only by TCP sockets.
+
+ After this function has been called, pending client connections
+ are detected by checking if the socket is ready for reading. This
+ can be done by either creating a QSocketNotifier, passing the
+ socket descriptor returned by socketDescriptor(), or by calling
+ the blocking function waitForRead().
+
+ Example:
+ \snippet doc/src/snippets/code/src_network_socket_qnativesocketengine.cpp 1
+
+ \sa bind(), accept()
+*/
+bool QNativeSocketEngine::listen()
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
+ Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
+
+ // We're using a backlog of 50. Most modern kernels support TCP
+ // syncookies by default, and if they do, the backlog is ignored.
+ // When there is no support for TCP syncookies, this value is
+ // fine.
+ return d->nativeListen(50);
+}
+
+/*!
+ Accepts a pending connection from the socket, which must be in
+ ListeningState, and returns its socket descriptor. If no pending
+ connections are available, -1 is returned.
+
+ \sa bind(), listen()
+*/
+int QNativeSocketEngine::accept()
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
+ Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, false);
+
+ 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.
+
+ For UDP sockets, this function returns the accumulated size of all
+ pending datagrams, and it is therefore more useful for UDP sockets
+ to call hasPendingDatagrams() and pendingDatagramSize().
+*/
+qint64 QNativeSocketEngine::bytesAvailable() const
+{
+ Q_D(const QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::bytesAvailable(), -1);
+ Q_CHECK_NOT_STATE(QNativeSocketEngine::bytesAvailable(), QAbstractSocket::UnconnectedState, false);
+
+ return d->nativeBytesAvailable();
+}
+
+/*!
+ Returns true if there is at least one datagram pending. This
+ function is only called by UDP sockets, where a datagram can have
+ a size of 0. TCP sockets call bytesAvailable().
+*/
+bool QNativeSocketEngine::hasPendingDatagrams() const
+{
+ Q_D(const QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::hasPendingDatagrams(), false);
+ Q_CHECK_NOT_STATE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UnconnectedState, false);
+ Q_CHECK_TYPE(QNativeSocketEngine::hasPendingDatagrams(), QAbstractSocket::UdpSocket, false);
+
+ return d->nativeHasPendingDatagrams();
+}
+
+/*!
+ Returns the size of the pending datagram, or -1 if no datagram is
+ pending. A datagram size of 0 is perfectly valid. This function is
+ called by UDP sockets before receiveMessage(). For TCP sockets,
+ call bytesAvailable().
+*/
+qint64 QNativeSocketEngine::pendingDatagramSize() const
+{
+ Q_D(const QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::pendingDatagramSize(), -1);
+ Q_CHECK_TYPE(QNativeSocketEngine::pendingDatagramSize(), QAbstractSocket::UdpSocket, false);
+
+ return d->nativePendingDatagramSize();
+}
+
+/*!
+ Reads up to \a maxSize bytes of a datagram from the socket,
+ stores it in \a data and returns the number of bytes read. The
+ address and port of the sender are stored in \a address and \a
+ port. If either of these pointers is 0, the corresponding value is
+ discarded.
+
+ To avoid unnecessarily loss of data, call pendingDatagramSize() to
+ determine the size of the pending message before reading it. If \a
+ maxSize is too small, the rest of the datagram will be lost.
+
+ Returns -1 if an error occurred.
+
+ \sa hasPendingDatagrams()
+*/
+qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QHostAddress *address,
+ quint16 *port)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1);
+ Q_CHECK_TYPE(QNativeSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, false);
+
+ return d->nativeReceiveDatagram(data, maxSize, address, port);
+}
+
+/*!
+ Writes a UDP datagram of size \a size bytes to the socket from
+ \a data to the address \a host on port \a port, and returns the
+ number of bytes written, or -1 if an error occurred.
+
+ Only one datagram is sent, and if there is too much data to fit
+ into a single datagram, the operation will fail and error()
+ will return QAbstractSocket::DatagramTooLargeError. Operating systems impose an
+ upper limit to the size of a datagram, but this size is different
+ on almost all platforms. Sending large datagrams is in general
+ disadvised, as even if they are sent successfully, they are likely
+ to be fragmented before arriving at their destination.
+
+ Experience has shown that it is in general safe to send datagrams
+ no larger than 512 bytes.
+
+ \sa readDatagram()
+*/
+qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 size,
+ const QHostAddress &host, quint16 port)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1);
+ Q_CHECK_TYPE(QNativeSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1);
+ return d->nativeSendDatagram(data, size, host, port);
+}
+
+/*!
+ Writes a block of \a size bytes from \a data to the socket.
+ Returns the number of bytes written, or -1 if an error occurred.
+*/
+qint64 QNativeSocketEngine::write(const char *data, qint64 size)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::write(), -1);
+ Q_CHECK_STATE(QNativeSocketEngine::write(), QAbstractSocket::ConnectedState, -1);
+ return d->nativeWrite(data, size);
+}
+
+
+qint64 QNativeSocketEngine::bytesToWrite() const
+{
+ return 0;
+}
+
+/*!
+ Reads up to \a maxSize bytes into \a data from the socket.
+ Returns the number of bytes read, or -1 if an error occurred.
+*/
+qint64 QNativeSocketEngine::read(char *data, qint64 maxSize)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::read(), -1);
+ Q_CHECK_STATES(QNativeSocketEngine::read(), QAbstractSocket::ConnectedState, QAbstractSocket::BoundState, -1);
+
+ qint64 readBytes = d->nativeRead(data, maxSize);
+
+ // Handle remote close
+ if (readBytes == 0 && d->socketType == QAbstractSocket::TcpSocket) {
+ d->setError(QAbstractSocket::RemoteHostClosedError,
+ QNativeSocketEnginePrivate::RemoteHostClosedErrorString);
+ close();
+ return -1;
+ } else if (readBytes == -1) {
+ if (!d->hasSetSocketError) {
+ d->hasSetSocketError = true;
+ d->socketError = QAbstractSocket::NetworkError;
+ d->socketErrorString = qt_error_string();
+ }
+ close();
+ return -1;
+ }
+ return readBytes;
+}
+
+/*!
+ Closes the socket. In order to use the socket again, initialize()
+ must be called.
+*/
+void QNativeSocketEngine::close()
+{
+ Q_D(QNativeSocketEngine);
+ if (d->readNotifier)
+ d->readNotifier->setEnabled(false);
+ if (d->writeNotifier)
+ d->writeNotifier->setEnabled(false);
+ if (d->exceptNotifier)
+ d->exceptNotifier->setEnabled(false);
+
+ if(d->socketDescriptor != -1) {
+ d->nativeClose();
+ d->socketDescriptor = -1;
+ }
+ d->socketState = QAbstractSocket::UnconnectedState;
+ d->hasSetSocketError = false;
+ d->localPort = 0;
+ d->localAddress.clear();
+ d->peerPort = 0;
+ d->peerAddress.clear();
+ if (d->readNotifier) {
+ qDeleteInEventHandler(d->readNotifier);
+ d->readNotifier = 0;
+ }
+ if (d->writeNotifier) {
+ qDeleteInEventHandler(d->writeNotifier);
+ d->writeNotifier = 0;
+ }
+ if (d->exceptNotifier) {
+ qDeleteInEventHandler(d->exceptNotifier);
+ d->exceptNotifier = 0;
+ }
+}
+
+/*!
+ Waits for \a msecs milliseconds or until the socket is ready for
+ reading. If \a timedOut is not 0 and \a msecs milliseconds have
+ passed, the value of \a timedOut is set to true.
+
+ Returns true if data is available for reading; otherwise returns
+ false.
+
+ This is a blocking function call; its use is disadvised in a
+ single threaded application, as the whole thread will stop
+ responding until the function returns. waitForRead() is most
+ useful when there is no event loop available. The general approach
+ is to create a QSocketNotifier, passing the socket descriptor
+ returned by socketDescriptor() to its constructor.
+*/
+bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
+{
+ Q_D(const QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
+ Q_CHECK_NOT_STATE(QNativeSocketEngine::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,
+ QNativeSocketEnginePrivate::TimeOutErrorString);
+ d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
+ return false;
+ } else if (state() == QAbstractSocket::ConnectingState) {
+ connectToHost(d->peerAddress, d->peerPort);
+ }
+
+ return ret > 0;
+}
+
+/*!
+ Waits for \a msecs milliseconds or until the socket is ready for
+ writing. If \a timedOut is not 0 and \a msecs milliseconds have
+ passed, the value of \a timedOut is set to true.
+
+ Returns true if data is available for writing; otherwise returns
+ false.
+
+ This is a blocking function call; its use is disadvised in a
+ single threaded application, as the whole thread will stop
+ responding until the function returns. waitForWrite() is most
+ useful when there is no event loop available. The general approach
+ is to create a QSocketNotifier, passing the socket descriptor
+ returned by socketDescriptor() to its constructor.
+*/
+bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
+ Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForWrite(),
+ QAbstractSocket::UnconnectedState, false);
+
+ if (timedOut)
+ *timedOut = false;
+
+ int ret = d->nativeSelect(msecs, false);
+ // On Windows, the socket is in connected state if a call to
+ // select(writable) is successful. In this case we should not
+ // issue a second call to WSAConnect()
+#if defined (Q_WS_WIN)
+ if (ret > 0) {
+ setState(QAbstractSocket::ConnectedState);
+ d_func()->fetchConnectionParameters();
+ return true;
+ } else {
+ int value = 0;
+ int valueSize = sizeof(value);
+ if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
+ if (value == WSAECONNREFUSED) {
+ d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAETIMEDOUT) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAEHOSTUNREACH) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
+ }
+ }
+#endif
+
+ if (ret == 0) {
+ if (timedOut)
+ *timedOut = true;
+ d->setError(QAbstractSocket::SocketTimeoutError,
+ QNativeSocketEnginePrivate::TimeOutErrorString);
+ d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
+ return false;
+ } else if (state() == QAbstractSocket::ConnectingState) {
+ connectToHost(d->peerAddress, d->peerPort);
+ }
+
+ return ret > 0;
+}
+
+bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs, bool *timedOut)
+{
+ Q_D(QNativeSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
+ Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(),
+ QAbstractSocket::UnconnectedState, false);
+
+ int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite);
+ // On Windows, the socket is in connected state if a call to
+ // select(writable) is successful. In this case we should not
+ // issue a second call to WSAConnect()
+#if defined (Q_WS_WIN)
+ if (checkWrite && ((readyToWrite && *readyToWrite) || !readyToWrite) && ret > 0) {
+ setState(QAbstractSocket::ConnectedState);
+ d_func()->fetchConnectionParameters();
+ return true;
+ } else {
+ int value = 0;
+ int valueSize = sizeof(value);
+ if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
+ if (value == WSAECONNREFUSED) {
+ d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAETIMEDOUT) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAEHOSTUNREACH) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
+ }
+ }
+#endif
+ if (ret == 0) {
+ if (timedOut)
+ *timedOut = true;
+ d->setError(QAbstractSocket::SocketTimeoutError,
+ QNativeSocketEnginePrivate::TimeOutErrorString);
+ d->hasSetSocketError = false; // A timeout error is temporary in waitFor functions
+ return false;
+ } else if (state() == QAbstractSocket::ConnectingState) {
+ connectToHost(d->peerAddress, d->peerPort);
+ }
+
+ return ret > 0;
+}
+
+/*!
+ Returns the size of the operating system's socket receive
+ buffer. Depending on the operating system, this size may be
+ different from what has been set earlier with
+ setReceiveBufferSize().
+*/
+qint64 QNativeSocketEngine::receiveBufferSize() const
+{
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::receiveBufferSize(), -1);
+ return option(ReceiveBufferSocketOption);
+}
+
+/*!
+ Sets the size of the operating system receive buffer to \a size.
+
+ For clients, this should be set before connectToHost() is called;
+ otherwise it will have no effect. For servers, it should be called
+ before listen().
+
+ The operating system receive buffer size effectively limits two
+ things: how much data can be in transit at any one moment, and how
+ much data can be received in one iteration of the main event loop.
+ Setting the size of the receive buffer may have an impact on the
+ socket's performance.
+
+ The default value is operating system-dependent.
+*/
+void QNativeSocketEngine::setReceiveBufferSize(qint64 size)
+{
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setReceiveBufferSize(), Q_VOID);
+ setOption(ReceiveBufferSocketOption, size);
+}
+
+/*!
+ Returns the size of the operating system send buffer. Depending on
+ the operating system, this size may be different from what has
+ been set earlier with setSendBufferSize().
+*/
+qint64 QNativeSocketEngine::sendBufferSize() const
+{
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setSendBufferSize(), -1);
+ return option(SendBufferSocketOption);
+}
+
+/*!
+ Sets the size of the operating system send buffer to \a size.
+
+ The operating system send buffer size effectively limits how much
+ data can be in transit at any one moment. Setting the size of the
+ send buffer may have an impact on the socket's performance.
+
+ The default value is operating system-dependent.
+*/
+void QNativeSocketEngine::setSendBufferSize(qint64 size)
+{
+ Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setSendBufferSize(), Q_VOID);
+ setOption(SendBufferSocketOption, size);
+}
+
+
+/*!
+ Sets the option \a option to the value \a value.
+*/
+bool QNativeSocketEngine::setOption(SocketOption option, int value)
+{
+ Q_D(QNativeSocketEngine);
+ return d->setOption(option, value);
+}
+
+/*!
+ Returns the value of the option \a socketOption.
+*/
+int QNativeSocketEngine::option(SocketOption socketOption) const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->option(socketOption);
+}
+
+bool QNativeSocketEngine::isReadNotificationEnabled() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->readNotifier && d->readNotifier->isEnabled();
+}
+
+/*
+ \internal
+ \class QReadNotifier
+ \brief The QReadNotifer class is used to improve performance.
+
+ QReadNotifier is a private class used for performance reasons vs
+ connecting to the QSocketNotifier activated() signal.
+ */
+class QReadNotifier : public QSocketNotifier
+{
+public:
+ QReadNotifier(int fd, QNativeSocketEngine *parent)
+ : QSocketNotifier(fd, QSocketNotifier::Read, parent)
+ { engine = parent; }
+
+protected:
+ bool event(QEvent *);
+
+ QNativeSocketEngine *engine;
+};
+
+bool QReadNotifier::event(QEvent *e)
+{
+ if (e->type() == QEvent::SockAct) {
+ engine->readNotification();
+ return true;
+ }
+ return QSocketNotifier::event(e);
+}
+
+/*
+ \internal
+ \class QWriteNotifier
+ \brief The QWriteNotifer class is used to improve performance.
+
+ QWriteNotifier is a private class used for performance reasons vs
+ connecting to the QSocketNotifier activated() signal.
+ */
+class QWriteNotifier : public QSocketNotifier
+{
+public:
+ QWriteNotifier(int fd, QNativeSocketEngine *parent)
+ : QSocketNotifier(fd, QSocketNotifier::Write, parent) { engine = parent; }
+
+protected:
+ bool event(QEvent *);
+
+ QNativeSocketEngine *engine;
+};
+
+bool QWriteNotifier::event(QEvent *e)
+{
+ if (e->type() == QEvent::SockAct) {
+ if (engine->state() == QAbstractSocket::ConnectingState)
+ engine->connectionNotification();
+ else
+ engine->writeNotification();
+ return true;
+ }
+ return QSocketNotifier::event(e);
+}
+
+class QExceptionNotifier : public QSocketNotifier
+{
+public:
+ QExceptionNotifier(int fd, QNativeSocketEngine *parent)
+ : QSocketNotifier(fd, QSocketNotifier::Exception, parent) { engine = parent; }
+
+protected:
+ bool event(QEvent *);
+
+ QNativeSocketEngine *engine;
+};
+
+bool QExceptionNotifier::event(QEvent *e)
+{
+ if (e->type() == QEvent::SockAct) {
+ if (engine->state() == QAbstractSocket::ConnectingState)
+ engine->connectionNotification();
+ else
+ engine->exceptionNotification();
+ return true;
+ }
+ return QSocketNotifier::event(e);
+}
+
+void QNativeSocketEngine::setReadNotificationEnabled(bool enable)
+{
+ Q_D(QNativeSocketEngine);
+ if (d->readNotifier) {
+ d->readNotifier->setEnabled(enable);
+ } else if (enable && d->threadData->eventDispatcher) {
+ d->readNotifier = new QReadNotifier(d->socketDescriptor, this);
+ d->readNotifier->setEnabled(true);
+ }
+}
+
+bool QNativeSocketEngine::isWriteNotificationEnabled() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->writeNotifier && d->writeNotifier->isEnabled();
+}
+
+void QNativeSocketEngine::setWriteNotificationEnabled(bool enable)
+{
+ Q_D(QNativeSocketEngine);
+ if (d->writeNotifier) {
+ d->writeNotifier->setEnabled(enable);
+ } else if (enable && d->threadData->eventDispatcher) {
+ d->writeNotifier = new QWriteNotifier(d->socketDescriptor, this);
+ d->writeNotifier->setEnabled(true);
+ }
+}
+
+bool QNativeSocketEngine::isExceptionNotificationEnabled() const
+{
+ Q_D(const QNativeSocketEngine);
+ return d->exceptNotifier && d->exceptNotifier->isEnabled();
+}
+
+void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable)
+{
+ Q_D(QNativeSocketEngine);
+ if (d->exceptNotifier) {
+ d->exceptNotifier->setEnabled(enable);
+ } else if (enable && d->threadData->eventDispatcher) {
+ d->exceptNotifier = new QExceptionNotifier(d->socketDescriptor, this);
+ d->exceptNotifier->setEnabled(true);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
new file mode 100644
index 0000000000..35054fb414
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNATIVESOCKETENGINE_P_H
+#define QNATIVESOCKETENGINE_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"
+#ifndef Q_OS_WIN
+# include "qplatformdefs.h"
+#else
+# include <winsock2.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Use our own defines and structs which we know are correct
+# define QT_SS_MAXSIZE 128
+# define QT_SS_ALIGNSIZE (sizeof(qint64))
+# define QT_SS_PAD1SIZE (QT_SS_ALIGNSIZE - sizeof (short))
+# define QT_SS_PAD2SIZE (QT_SS_MAXSIZE - (sizeof (short) + QT_SS_PAD1SIZE + QT_SS_ALIGNSIZE))
+struct qt_sockaddr_storage {
+ short ss_family;
+ char __ss_pad1[QT_SS_PAD1SIZE];
+ qint64 __ss_align;
+ char __ss_pad2[QT_SS_PAD2SIZE];
+};
+
+// sockaddr_in6 size changed between old and new SDK
+// Only the new version is the correct one, so always
+// use this structure.
+struct qt_in6_addr {
+ quint8 qt_s6_addr[16];
+};
+struct qt_sockaddr_in6 {
+ short sin6_family; /* AF_INET6 */
+ quint16 sin6_port; /* Transport level port number */
+ quint32 sin6_flowinfo; /* IPv6 flow information */
+ struct qt_in6_addr sin6_addr; /* IPv6 address */
+ quint32 sin6_scope_id; /* set of interfaces for a scope */
+};
+
+union qt_sockaddr {
+ sockaddr a;
+ sockaddr_in a4;
+ qt_sockaddr_in6 a6;
+ qt_sockaddr_storage storage;
+};
+
+class QNativeSocketEnginePrivate;
+#ifndef QT_NO_NETWORKINTERFACE
+class QNetworkInterface;
+#endif
+
+class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine
+{
+ Q_OBJECT
+public:
+ QNativeSocketEngine(QObject *parent = 0);
+ ~QNativeSocketEngine();
+
+ 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();
+
+#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);
+ 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);
+
+public Q_SLOTS:
+ // non-virtual override;
+ void connectionNotification();
+
+private:
+ Q_DECLARE_PRIVATE(QNativeSocketEngine)
+ Q_DISABLE_COPY(QNativeSocketEngine)
+};
+
+#ifdef Q_OS_WIN
+class QWindowsSockInit
+{
+public:
+ QWindowsSockInit();
+ ~QWindowsSockInit();
+ int version;
+};
+#endif
+
+class QSocketNotifier;
+
+class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QNativeSocketEngine)
+public:
+ QNativeSocketEnginePrivate();
+ ~QNativeSocketEnginePrivate();
+
+ int socketDescriptor;
+
+ QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
+
+#ifdef Q_OS_WIN
+ QWindowsSockInit winSock;
+#endif
+
+ 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,
+
+ UnknownSocketErrorString = -1
+ };
+
+ void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
+
+ // native functions
+ int option(QNativeSocketEngine::SocketOption option) const;
+ bool setOption(QNativeSocketEngine::SocketOption option, int value);
+
+ bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol);
+
+ bool nativeConnect(const QHostAddress &address, quint16 port);
+ 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;
+ qint64 nativePendingDatagramSize() const;
+ qint64 nativeReceiveDatagram(char *data, qint64 maxLength,
+ QHostAddress *address, quint16 *port);
+ qint64 nativeSendDatagram(const char *data, qint64 length,
+ const QHostAddress &host, quint16 port);
+ qint64 nativeRead(char *data, qint64 maxLength);
+ qint64 nativeWrite(const char *data, qint64 length);
+ int nativeSelect(int timeout, bool selectForRead) const;
+ int nativeSelect(int timeout, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const;
+
+ void nativeClose();
+
+ bool checkProxy(const QHostAddress &address);
+ bool fetchConnectionParameters();
+};
+
+QT_END_NAMESPACE
+
+#endif // QNATIVESOCKETENGINE_P_H
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
new file mode 100644
index 0000000000..43184271e5
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -0,0 +1,1125 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNATIVESOCKETENGINE_DEBUG
+#include "qnativesocketengine_p.h"
+#include "private/qnet_unix_p.h"
+#include "qiodevice.h"
+#include "qhostaddress.h"
+#include "qelapsedtimer.h"
+#include "qvarlengtharray.h"
+#include "qnetworkinterface.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifndef QT_NO_IPV6IFNAME
+#include <net/if.h>
+#endif
+#ifndef QT_NO_IPV6IFNAME
+#include <net/if.h>
+#endif
+#ifdef QT_LINUXBASE
+#include <arpa/inet.h>
+#endif
+
+#if defined QNATIVESOCKETENGINE_DEBUG
+#include <qstring.h>
+#include <ctype.h>
+#endif
+
+#include <netinet/tcp.h>
+
+QT_BEGIN_NAMESPACE
+
+#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
+
+static void qt_ignore_sigpipe()
+{
+#ifndef Q_NO_POSIX_SIGNALS
+ // Set to ignore SIGPIPE once only.
+ static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (atom.testAndSetRelaxed(0, 1)) {
+ struct sigaction noaction;
+ memset(&noaction, 0, sizeof(noaction));
+ noaction.sa_handler = SIG_IGN;
+ ::sigaction(SIGPIPE, &noaction, 0);
+ }
+#else
+ // Posix signals are not supported by the underlying platform
+ // so we don't need to ignore sigpipe signal explicitly
+#endif
+}
+
+/*
+ Extracts the port and address from a sockaddr, and stores them in
+ \a port and \a addr if they are non-null.
+*/
+static inline void qt_socket_getPortAndAddress(const qt_sockaddr *s, quint16 *port, QHostAddress *addr)
+{
+#if !defined(QT_NO_IPV6)
+ if (s->a.sa_family == AF_INET6) {
+ Q_IPV6ADDR tmp;
+ memcpy(&tmp, &s->a6.sin6_addr, sizeof(tmp));
+ if (addr) {
+ QHostAddress tmpAddress;
+ tmpAddress.setAddress(tmp);
+ *addr = tmpAddress;
+#ifndef QT_NO_IPV6IFNAME
+ char scopeid[IFNAMSIZ];
+ if (::if_indextoname(s->a6.sin6_scope_id, scopeid)) {
+ addr->setScopeId(QLatin1String(scopeid));
+ } else
+#endif
+ addr->setScopeId(QString::number(s->a6.sin6_scope_id));
+ }
+ if (port)
+ *port = ntohs(s->a6.sin6_port);
+ return;
+ }
+#endif
+ if (port)
+ *port = ntohs(s->a4.sin_port);
+ if (addr) {
+ QHostAddress tmpAddress;
+ tmpAddress.setAddress(ntohl(s->a4.sin_addr.s_addr));
+ *addr = tmpAddress;
+ }
+}
+
+/*! \internal
+
+ Creates and returns a new socket descriptor of type \a socketType
+ and \a socketProtocol. Returns -1 on failure.
+*/
+bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType,
+ QAbstractSocket::NetworkLayerProtocol socketProtocol)
+{
+#ifndef QT_NO_IPV6
+ int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol) ? AF_INET6 : AF_INET;
+#else
+ Q_UNUSED(socketProtocol);
+ int protocol = AF_INET;
+#endif
+ int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
+
+ int socket = qt_safe_socket(protocol, type, 0);
+
+ if (socket <= 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ case EAFNOSUPPORT:
+ case EINVAL:
+ setError(QAbstractSocket::UnsupportedSocketOperationError, ProtocolUnsupportedErrorString);
+ break;
+ case ENFILE:
+ case EMFILE:
+ case ENOBUFS:
+ case ENOMEM:
+ setError(QAbstractSocket::SocketResourceError, ResourceErrorString);
+ break;
+ case EACCES:
+ setError(QAbstractSocket::SocketAccessError, AccessErrorString);
+ break;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ socketDescriptor = socket;
+ return true;
+}
+
+/*
+ Returns the value of the socket option \a opt.
+*/
+int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) const
+{
+ Q_Q(const QNativeSocketEngine);
+ if (!q->isValid())
+ return -1;
+
+ int n = -1;
+ int level = SOL_SOCKET; // default
+
+ switch (opt) {
+ case QNativeSocketEngine::ReceiveBufferSocketOption:
+ n = SO_RCVBUF;
+ break;
+ case QNativeSocketEngine::SendBufferSocketOption:
+ n = SO_SNDBUF;
+ break;
+ case QNativeSocketEngine::NonBlockingSocketOption:
+ break;
+ case QNativeSocketEngine::BroadcastSocketOption:
+ break;
+ case QNativeSocketEngine::AddressReusable:
+ n = SO_REUSEADDR;
+ break;
+ case QNativeSocketEngine::BindExclusively:
+ return true;
+ case QNativeSocketEngine::ReceiveOutOfBandData:
+ n = SO_OOBINLINE;
+ break;
+ case QNativeSocketEngine::LowDelayOption:
+ level = IPPROTO_TCP;
+ n = TCP_NODELAY;
+ break;
+ 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;
+ QT_SOCKOPTLEN_T len = sizeof(v);
+ if (::getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1)
+ return v;
+
+ return -1;
+}
+
+
+/*
+ Sets the socket option \a opt to \a v.
+*/
+bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt, int v)
+{
+ Q_Q(QNativeSocketEngine);
+ if (!q->isValid())
+ return false;
+
+ int n = 0;
+ int level = SOL_SOCKET; // default
+
+ switch (opt) {
+ case QNativeSocketEngine::ReceiveBufferSocketOption:
+ n = SO_RCVBUF;
+ break;
+ case QNativeSocketEngine::SendBufferSocketOption:
+ n = SO_SNDBUF;
+ break;
+ case QNativeSocketEngine::BroadcastSocketOption:
+ n = SO_BROADCAST;
+ break;
+ case QNativeSocketEngine::NonBlockingSocketOption: {
+ // Make the socket nonblocking.
+#if !defined(Q_OS_VXWORKS)
+ int flags = ::fcntl(socketDescriptor, F_GETFL, 0);
+ if (flags == -1) {
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ perror("QNativeSocketEnginePrivate::setOption(): fcntl(F_GETFL) failed");
+#endif
+ return false;
+ }
+ if (::fcntl(socketDescriptor, F_SETFL, flags | O_NONBLOCK) == -1) {
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ perror("QNativeSocketEnginePrivate::setOption(): fcntl(F_SETFL) failed");
+#endif
+ return false;
+ }
+#else // Q_OS_VXWORKS
+ int onoff = 1;
+
+ if (qt_safe_ioctl(socketDescriptor, FIONBIO, &onoff) < 0) {
+
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ perror("QNativeSocketEnginePrivate::setOption(): ioctl(FIONBIO, 1) failed");
+#endif
+ return false;
+ }
+#endif // Q_OS_VXWORKS
+ return true;
+ }
+ case QNativeSocketEngine::AddressReusable:
+#if defined(SO_REUSEPORT)
+ n = SO_REUSEPORT;
+#else
+ n = SO_REUSEADDR;
+#endif
+ break;
+ case QNativeSocketEngine::BindExclusively:
+ return true;
+ case QNativeSocketEngine::ReceiveOutOfBandData:
+ n = SO_OOBINLINE;
+ break;
+ case QNativeSocketEngine::LowDelayOption:
+ level = IPPROTO_TCP;
+ n = TCP_NODELAY;
+ break;
+ 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;
+}
+
+bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16 port)
+{
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug("QNativeSocketEnginePrivate::nativeConnect() : %d ", socketDescriptor);
+#endif
+
+ struct sockaddr_in sockAddrIPv4;
+ struct sockaddr *sockAddrPtr = 0;
+ QT_SOCKLEN_T sockAddrSize = 0;
+
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 sockAddrIPv6;
+
+ if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
+ memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+ sockAddrIPv6.sin6_family = AF_INET6;
+ sockAddrIPv6.sin6_port = htons(port);
+
+ QString scopeid = addr.scopeId();
+ bool ok;
+ sockAddrIPv6.sin6_scope_id = scopeid.toInt(&ok);
+#ifndef QT_NO_IPV6IFNAME
+ if (!ok)
+ sockAddrIPv6.sin6_scope_id = ::if_nametoindex(scopeid.toLatin1());
+#endif
+ Q_IPV6ADDR ip6 = addr.toIPv6Address();
+ memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
+
+ sockAddrSize = sizeof(sockAddrIPv6);
+ sockAddrPtr = (struct sockaddr *) &sockAddrIPv6;
+ } else
+#if 0
+ {}
+#endif
+#endif
+ if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
+ memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+ sockAddrIPv4.sin_family = AF_INET;
+ sockAddrIPv4.sin_port = htons(port);
+ sockAddrIPv4.sin_addr.s_addr = htonl(addr.toIPv4Address());
+
+ sockAddrSize = sizeof(sockAddrIPv4);
+ sockAddrPtr = (struct sockaddr *) &sockAddrIPv4;
+ } else {
+ // unreachable
+ }
+
+ int connectResult = qt_safe_connect(socketDescriptor, sockAddrPtr, sockAddrSize);
+ if (connectResult == -1) {
+ switch (errno) {
+ case EISCONN:
+ socketState = QAbstractSocket::ConnectedState;
+ break;
+ case ECONNREFUSED:
+ case EINVAL:
+ setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case ETIMEDOUT:
+ setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
+ break;
+ case EHOSTUNREACH:
+ setError(QAbstractSocket::NetworkError, HostUnreachableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case ENETUNREACH:
+ setError(QAbstractSocket::NetworkError, NetworkUnreachableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case EADDRINUSE:
+ setError(QAbstractSocket::NetworkError, AddressInuseErrorString);
+ break;
+ case EINPROGRESS:
+ case EALREADY:
+ setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
+ socketState = QAbstractSocket::ConnectingState;
+ break;
+ case EAGAIN:
+ setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
+ setError(QAbstractSocket::SocketResourceError, ResourceErrorString);
+ break;
+ case EACCES:
+ case EPERM:
+ setError(QAbstractSocket::SocketAccessError, AccessErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case EAFNOSUPPORT:
+ case EBADF:
+ case EFAULT:
+ case ENOTSOCK:
+ socketState = QAbstractSocket::UnconnectedState;
+ default:
+ break;
+ }
+
+ if (socketState != QAbstractSocket::ConnectedState) {
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeConnect(%s, %i) == false (%s)",
+ addr.toString().toLatin1().constData(), port,
+ socketState == QAbstractSocket::ConnectingState
+ ? "Connection in progress" : socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeConnect(%s, %i) == true",
+ addr.toString().toLatin1().constData(), port);
+#endif
+
+ socketState = QAbstractSocket::ConnectedState;
+ return true;
+}
+
+bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 port)
+{
+ struct sockaddr_in sockAddrIPv4;
+ struct sockaddr *sockAddrPtr = 0;
+ QT_SOCKLEN_T sockAddrSize = 0;
+
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 sockAddrIPv6;
+
+ if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+ sockAddrIPv6.sin6_family = AF_INET6;
+ sockAddrIPv6.sin6_port = htons(port);
+#ifndef QT_NO_IPV6IFNAME
+ sockAddrIPv6.sin6_scope_id = ::if_nametoindex(address.scopeId().toLatin1().data());
+#else
+ sockAddrIPv6.sin6_scope_id = address.scopeId().toInt();
+#endif
+ Q_IPV6ADDR tmp = address.toIPv6Address();
+ memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp));
+ sockAddrSize = sizeof(sockAddrIPv6);
+ sockAddrPtr = (struct sockaddr *) &sockAddrIPv6;
+ } else
+#endif
+ if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+ memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+ sockAddrIPv4.sin_family = AF_INET;
+ sockAddrIPv4.sin_port = htons(port);
+ sockAddrIPv4.sin_addr.s_addr = htonl(address.toIPv4Address());
+ sockAddrSize = sizeof(sockAddrIPv4);
+ sockAddrPtr = (struct sockaddr *) &sockAddrIPv4;
+ } else {
+ // unreachable
+ }
+
+ int bindResult = QT_SOCKET_BIND(socketDescriptor, sockAddrPtr, sockAddrSize);
+
+ if (bindResult < 0) {
+ switch(errno) {
+ case EADDRINUSE:
+ setError(QAbstractSocket::AddressInUseError, AddressInuseErrorString);
+ break;
+ case EACCES:
+ setError(QAbstractSocket::SocketAccessError, AddressProtectedErrorString);
+ break;
+ case EINVAL:
+ setError(QAbstractSocket::UnsupportedSocketOperationError, OperationUnsupportedErrorString);
+ break;
+ case EADDRNOTAVAIL:
+ setError(QAbstractSocket::SocketAddressNotAvailableError, AddressNotAvailableErrorString);
+ break;
+ default:
+ break;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeBind(%s, %i) == false (%s)",
+ address.toString().toLatin1().constData(), port, socketErrorString.toLatin1().constData());
+#endif
+
+ return false;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeBind(%s, %i) == true",
+ address.toString().toLatin1().constData(), port);
+#endif
+ socketState = QAbstractSocket::BoundState;
+ return true;
+}
+
+bool QNativeSocketEnginePrivate::nativeListen(int backlog)
+{
+ if (qt_safe_listen(socketDescriptor, backlog) < 0) {
+ switch (errno) {
+ case EADDRINUSE:
+ setError(QAbstractSocket::AddressInUseError,
+ PortInuseErrorString);
+ break;
+ default:
+ break;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeListen(%i) == false (%s)",
+ backlog, socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeListen(%i) == true", backlog);
+#endif
+
+ socketState = QAbstractSocket::ListeningState;
+ return true;
+}
+
+int QNativeSocketEnginePrivate::nativeAccept()
+{
+ int acceptedDescriptor = qt_safe_accept(socketDescriptor, 0, 0);
+
+ 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;
+ if (qt_safe_ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0)
+ available = (qint64) nbytes;
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeBytesAvailable() == %lli", available);
+#endif
+ return available;
+}
+
+bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
+{
+ // Create a sockaddr struct and reset its port number.
+ qt_sockaddr storage;
+ QT_SOCKLEN_T storageSize = sizeof(storage);
+ memset(&storage, 0, storageSize);
+
+ // 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;
+ do {
+ char c;
+ readBytes = ::recvfrom(socketDescriptor, &c, 1, MSG_PEEK, &storage.a, &storageSize);
+ } while (readBytes == -1 && errno == EINTR);
+
+ // If there's no error, or if our buffer was too small, there must be a
+ // pending datagram.
+ bool result = (readBytes != -1) || errno == EMSGSIZE;
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeHasPendingDatagrams() == %s",
+ result ? "true" : "false");
+#endif
+ return result;
+}
+
+qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
+{
+ QVarLengthArray<char, 8192> udpMessagePeekBuffer(8192);
+ ssize_t recvResult = -1;
+
+ for (;;) {
+ // the data written to udpMessagePeekBuffer is discarded, so
+ // this function is still reentrant although it might not look
+ // so.
+ recvResult = ::recv(socketDescriptor, udpMessagePeekBuffer.data(),
+ udpMessagePeekBuffer.size(), MSG_PEEK);
+ if (recvResult == -1 && errno == EINTR)
+ continue;
+
+ if (recvResult != (ssize_t) udpMessagePeekBuffer.size())
+ break;
+
+ udpMessagePeekBuffer.resize(udpMessagePeekBuffer.size() * 2);
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %i", recvResult);
+#endif
+
+ return qint64(recvResult);
+}
+
+qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxSize,
+ QHostAddress *address, quint16 *port)
+{
+ qt_sockaddr aa;
+ memset(&aa, 0, sizeof(aa));
+ QT_SOCKLEN_T sz;
+ sz = sizeof(aa);
+
+ ssize_t recvFromResult = 0;
+ do {
+ char c;
+ recvFromResult = ::recvfrom(socketDescriptor, maxSize ? data : &c, maxSize ? maxSize : 1,
+ 0, &aa.a, &sz);
+ } while (recvFromResult == -1 && errno == EINTR);
+
+ if (recvFromResult == -1) {
+ setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
+ } else if (port || address) {
+ qt_socket_getPortAndAddress(&aa, port, address);
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %lli, %s, %i) == %lli",
+ data, qt_prettyDebug(data, qMin(recvFromResult, ssize_t(16)), recvFromResult).data(), maxSize,
+ address ? address->toString().toLatin1().constData() : "(nil)",
+ port ? *port : 0, (qint64) recvFromResult);
+#endif
+
+ return qint64(maxSize ? recvFromResult : recvFromResult == -1 ? -1 : 0);
+}
+
+qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len,
+ const QHostAddress &host, quint16 port)
+{
+ struct sockaddr_in sockAddrIPv4;
+ struct sockaddr *sockAddrPtr = 0;
+ QT_SOCKLEN_T sockAddrSize = 0;
+
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 sockAddrIPv6;
+ if (host.protocol() == QAbstractSocket::IPv6Protocol) {
+ memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+ sockAddrIPv6.sin6_family = AF_INET6;
+ sockAddrIPv6.sin6_port = htons(port);
+
+ Q_IPV6ADDR tmp = host.toIPv6Address();
+ memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &tmp, sizeof(tmp));
+ sockAddrSize = sizeof(sockAddrIPv6);
+ sockAddrPtr = (struct sockaddr *)&sockAddrIPv6;
+ } else
+#endif
+ if (host.protocol() == QAbstractSocket::IPv4Protocol) {
+ memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+ sockAddrIPv4.sin_family = AF_INET;
+ sockAddrIPv4.sin_port = htons(port);
+ sockAddrIPv4.sin_addr.s_addr = htonl(host.toIPv4Address());
+ sockAddrSize = sizeof(sockAddrIPv4);
+ sockAddrPtr = (struct sockaddr *)&sockAddrIPv4;
+ }
+
+ // ignore the SIGPIPE signal
+ qt_ignore_sigpipe();
+ ssize_t sentBytes = qt_safe_sendto(socketDescriptor, data, len,
+ 0, sockAddrPtr, sockAddrSize);
+
+ if (sentBytes < 0) {
+ switch (errno) {
+ case EMSGSIZE:
+ setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString);
+ break;
+ default:
+ setError(QAbstractSocket::NetworkError, SendDatagramErrorString);
+ }
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEngine::sendDatagram(%p \"%s\", %lli, \"%s\", %i) == %lli", data,
+ qt_prettyDebug(data, qMin<int>(len, 16), len).data(), len, host.toString().toLatin1().constData(),
+ port, (qint64) sentBytes);
+#endif
+
+ return qint64(sentBytes);
+}
+
+bool QNativeSocketEnginePrivate::fetchConnectionParameters()
+{
+ localPort = 0;
+ localAddress.clear();
+ peerPort = 0;
+ peerAddress.clear();
+
+ if (socketDescriptor == -1)
+ return false;
+
+ qt_sockaddr sa;
+ QT_SOCKLEN_T sockAddrSize = sizeof(sa);
+
+ // Determine local address
+ memset(&sa, 0, sizeof(sa));
+ if (::getsockname(socketDescriptor, &sa.a, &sockAddrSize) == 0) {
+ qt_socket_getPortAndAddress(&sa, &localPort, &localAddress);
+
+ // Determine protocol family
+ switch (sa.a.sa_family) {
+ case AF_INET:
+ socketProtocol = QAbstractSocket::IPv4Protocol;
+ break;
+#if !defined (QT_NO_IPV6)
+ case AF_INET6:
+ socketProtocol = QAbstractSocket::IPv6Protocol;
+ break;
+#endif
+ default:
+ socketProtocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ break;
+ }
+
+ } else if (errno == EBADF) {
+ setError(QAbstractSocket::UnsupportedSocketOperationError, InvalidSocketErrorString);
+ return false;
+ }
+
+ // Determine the remote address
+ if (!::getpeername(socketDescriptor, &sa.a, &sockAddrSize))
+ qt_socket_getPortAndAddress(&sa, &peerPort, &peerAddress);
+
+ // Determine the socket type (UDP/TCP)
+ int value = 0;
+ QT_SOCKOPTLEN_T valueSize = sizeof(int);
+ if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_TYPE, &value, &valueSize) == 0) {
+ if (value == SOCK_STREAM)
+ socketType = QAbstractSocket::TcpSocket;
+ else if (value == SOCK_DGRAM)
+ socketType = QAbstractSocket::UdpSocket;
+ else
+ socketType = QAbstractSocket::UnknownSocketType;
+ }
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ QString socketProtocolStr = "UnknownProtocol";
+ if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = "IPv4Protocol";
+ else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = "IPv6Protocol";
+
+ QString socketTypeStr = "UnknownSocketType";
+ if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = "TcpSocket";
+ else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = "UdpSocket";
+
+ qDebug("QNativeSocketEnginePrivate::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 QNativeSocketEnginePrivate::nativeClose()
+{
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEngine::nativeClose()");
+#endif
+
+ qt_safe_close(socketDescriptor);
+}
+
+qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
+{
+ Q_Q(QNativeSocketEngine);
+
+ // ignore the SIGPIPE signal
+ qt_ignore_sigpipe();
+
+ ssize_t writtenBytes;
+ writtenBytes = qt_safe_write(socketDescriptor, data, len);
+
+ if (writtenBytes < 0) {
+ switch (errno) {
+ case EPIPE:
+ case ECONNRESET:
+ writtenBytes = -1;
+ setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
+ q->close();
+ break;
+ case EAGAIN:
+ writtenBytes = 0;
+ break;
+ case EMSGSIZE:
+ setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString);
+ break;
+ default:
+ break;
+ }
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %llu) == %i",
+ data, qt_prettyDebug(data, qMin((int) len, 16),
+ (int) len).data(), len, (int) writtenBytes);
+#endif
+
+ return qint64(writtenBytes);
+}
+/*
+*/
+qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
+{
+ Q_Q(QNativeSocketEngine);
+ if (!q->isValid()) {
+ qWarning("QNativeSocketEngine::nativeRead: Invalid socket");
+ return -1;
+ }
+
+ ssize_t r = 0;
+ r = qt_safe_read(socketDescriptor, data, maxSize);
+
+ if (r < 0) {
+ r = -1;
+ switch (errno) {
+#if EWOULDBLOCK-0 && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ // No data was available for reading
+ r = -2;
+ break;
+ case EBADF:
+ case EINVAL:
+ case EIO:
+ //error string is now set in read(), not here in nativeRead()
+ break;
+ case ECONNRESET:
+#if defined(Q_OS_VXWORKS)
+ case ESHUTDOWN:
+#endif
+ r = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %llu) == %i",
+ data, qt_prettyDebug(data, qMin(r, ssize_t(16)), r).data(),
+ maxSize, r);
+#endif
+
+ return qint64(r);
+}
+
+int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+{
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(socketDescriptor, &fds);
+
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ int retval;
+ if (selectForRead)
+ retval = qt_safe_select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv);
+ else
+ retval = qt_safe_select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv);
+
+ return retval;
+}
+
+int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const
+{
+ fd_set fdread;
+ FD_ZERO(&fdread);
+ if (checkRead)
+ FD_SET(socketDescriptor, &fdread);
+
+ fd_set fdwrite;
+ FD_ZERO(&fdwrite);
+ if (checkWrite)
+ FD_SET(socketDescriptor, &fdwrite);
+
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ int ret;
+ ret = qt_safe_select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv);
+
+ if (ret <= 0)
+ return ret;
+ *selectForRead = FD_ISSET(socketDescriptor, &fdread);
+ *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite);
+
+ return ret;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
new file mode 100644
index 0000000000..940569aa7b
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -0,0 +1,1439 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include "qnativesocketengine_p.h"
+
+#include <qabstracteventdispatcher.h>
+#include <qsocketnotifier.h>
+#include <qdebug.h>
+#include <qdatetime.h>
+#include <qnetworkinterface.h>
+
+//#define QNATIVESOCKETENGINE_DEBUG
+#if defined(QNATIVESOCKETENGINE_DEBUG)
+# include <qstring.h>
+# include <qbytearray.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#if defined(QNATIVESOCKETENGINE_DEBUG)
+
+void verboseWSErrorDebug(int r)
+{
+ switch (r) {
+ case WSANOTINITIALISED : qDebug("WSA error : WSANOTINITIALISED"); break;
+ case WSAEINTR: qDebug("WSA error : WSAEINTR"); break;
+ case WSAEBADF: qDebug("WSA error : WSAEBADF"); break;
+ case WSAEACCES: qDebug("WSA error : WSAEACCES"); break;
+ case WSAEFAULT: qDebug("WSA error : WSAEFAULT"); break;
+ case WSAEINVAL: qDebug("WSA error : WSAEINVAL"); break;
+ case WSAEMFILE: qDebug("WSA error : WSAEMFILE"); break;
+ case WSAEWOULDBLOCK: qDebug("WSA error : WSAEWOULDBLOCK"); break;
+ case WSAEINPROGRESS: qDebug("WSA error : WSAEINPROGRESS"); break;
+ case WSAEALREADY: qDebug("WSA error : WSAEALREADY"); break;
+ case WSAENOTSOCK: qDebug("WSA error : WSAENOTSOCK"); break;
+ case WSAEDESTADDRREQ: qDebug("WSA error : WSAEDESTADDRREQ"); break;
+ case WSAEMSGSIZE: qDebug("WSA error : WSAEMSGSIZE"); break;
+ case WSAEPROTOTYPE: qDebug("WSA error : WSAEPROTOTYPE"); break;
+ case WSAENOPROTOOPT: qDebug("WSA error : WSAENOPROTOOPT"); break;
+ case WSAEPROTONOSUPPORT: qDebug("WSA error : WSAEPROTONOSUPPORT"); break;
+ case WSAESOCKTNOSUPPORT: qDebug("WSA error : WSAESOCKTNOSUPPORT"); break;
+ case WSAEOPNOTSUPP: qDebug("WSA error : WSAEOPNOTSUPP"); break;
+ case WSAEPFNOSUPPORT: qDebug("WSA error : WSAEPFNOSUPPORT"); break;
+ case WSAEAFNOSUPPORT: qDebug("WSA error : WSAEAFNOSUPPORT"); break;
+ case WSAEADDRINUSE: qDebug("WSA error : WSAEADDRINUSE"); break;
+ case WSAEADDRNOTAVAIL: qDebug("WSA error : WSAEADDRNOTAVAIL"); break;
+ case WSAENETDOWN: qDebug("WSA error : WSAENETDOWN"); break;
+ case WSAENETUNREACH: qDebug("WSA error : WSAENETUNREACH"); break;
+ case WSAENETRESET: qDebug("WSA error : WSAENETRESET"); break;
+ case WSAECONNABORTED: qDebug("WSA error : WSAECONNABORTED"); break;
+ case WSAECONNRESET: qDebug("WSA error : WSAECONNRESET"); break;
+ case WSAENOBUFS: qDebug("WSA error : WSAENOBUFS"); break;
+ case WSAEISCONN: qDebug("WSA error : WSAEISCONN"); break;
+ case WSAENOTCONN: qDebug("WSA error : WSAENOTCONN"); break;
+ case WSAESHUTDOWN: qDebug("WSA error : WSAESHUTDOWN"); break;
+ case WSAETOOMANYREFS: qDebug("WSA error : WSAETOOMANYREFS"); break;
+ case WSAETIMEDOUT: qDebug("WSA error : WSAETIMEDOUT"); break;
+ case WSAECONNREFUSED: qDebug("WSA error : WSAECONNREFUSED"); break;
+ case WSAELOOP: qDebug("WSA error : WSAELOOP"); break;
+ case WSAENAMETOOLONG: qDebug("WSA error : WSAENAMETOOLONG"); break;
+ case WSAEHOSTDOWN: qDebug("WSA error : WSAEHOSTDOWN"); break;
+ case WSAEHOSTUNREACH: qDebug("WSA error : WSAEHOSTUNREACH"); break;
+ case WSAENOTEMPTY: qDebug("WSA error : WSAENOTEMPTY"); break;
+ case WSAEPROCLIM: qDebug("WSA error : WSAEPROCLIM"); break;
+ case WSAEUSERS: qDebug("WSA error : WSAEUSERS"); break;
+ case WSAEDQUOT: qDebug("WSA error : WSAEDQUOT"); break;
+ case WSAESTALE: qDebug("WSA error : WSAESTALE"); break;
+ case WSAEREMOTE: qDebug("WSA error : WSAEREMOTE"); break;
+ case WSAEDISCON: qDebug("WSA error : WSAEDISCON"); break;
+ default: qDebug("WSA error : Unknown"); break;
+ }
+ qErrnoWarning(r, "more details");
+}
+
+/*
+ Returns a human readable representation of the first \a len
+ characters in \a data.
+*/
+static QByteArray qt_prettyDebug(const char *data, int len, int maxLength)
+{
+ if (!data) return "(null)";
+ QByteArray out;
+ for (int i = 0; i < len; ++i) {
+ char c = data[i];
+ if (isprint(int(uchar(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().constData();
+ }
+ }
+
+ if (len < maxLength)
+ out += "...";
+
+ return out;
+}
+
+
+#define WS_ERROR_DEBUG(x) verboseWSErrorDebug(x);
+
+#else
+
+#define WS_ERROR_DEBUG(x) Q_UNUSED(x)
+
+#endif
+
+#ifndef AF_INET6
+#define AF_INET6 23 /* Internetwork Version 6 */
+#endif
+
+#ifndef SO_EXCLUSIVEADDRUSE
+#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR)) /* disallow local address reuse */
+#endif
+
+//###
+#define QT_SOCKLEN_T int
+#define QT_SOCKOPTLEN_T int
+
+
+/*
+ Extracts the port and address from a sockaddr, and stores them in
+ \a port and \a addr if they are non-null.
+*/
+static inline void qt_socket_getPortAndAddress(SOCKET socketDescriptor, const qt_sockaddr *sa, quint16 *port, QHostAddress *address)
+{
+#if !defined (QT_NO_IPV6)
+ if (sa->a.sa_family == AF_INET6) {
+ const qt_sockaddr_in6 *sa6 = &sa->a6;
+ Q_IPV6ADDR tmp;
+ for (int i = 0; i < 16; ++i)
+ tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i];
+ QHostAddress a;
+ a.setAddress(tmp);
+ if (address)
+ *address = a;
+ if (port)
+ WSANtohs(socketDescriptor, sa6->sin6_port, port);
+ } else
+#endif
+ if (sa->a.sa_family == AF_INET) {
+ const sockaddr_in *sa4 = &sa->a4;
+ unsigned long addr;
+ WSANtohl(socketDescriptor, sa4->sin_addr.s_addr, &addr);
+ QHostAddress a;
+ a.setAddress(addr);
+ if (address)
+ *address = a;
+ if (port)
+ WSANtohs(socketDescriptor, sa4->sin_port, port);
+ }
+}
+
+
+/*! \internal
+
+ Sets the port and address to a sockaddr. Requires that sa point to the IPv6 struct if the address is IPv6.
+*/
+static inline void qt_socket_setPortAndAddress(SOCKET socketDescriptor, sockaddr_in * sockAddrIPv4, qt_sockaddr_in6 * sockAddrIPv6,
+ quint16 port, const QHostAddress & address, sockaddr ** sockAddrPtr, QT_SOCKLEN_T *sockAddrSize)
+{
+#if !defined(QT_NO_IPV6)
+ if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ memset(sockAddrIPv6, 0, sizeof(qt_sockaddr_in6));
+ sockAddrIPv6->sin6_family = AF_INET6;
+ sockAddrIPv6->sin6_scope_id = address.scopeId().toInt();
+ WSAHtons(socketDescriptor, port, &(sockAddrIPv6->sin6_port));
+ Q_IPV6ADDR tmp = address.toIPv6Address();
+ memcpy(&(sockAddrIPv6->sin6_addr.qt_s6_addr), &tmp, sizeof(tmp));
+ *sockAddrSize = sizeof(qt_sockaddr_in6);
+ *sockAddrPtr = (struct sockaddr *) sockAddrIPv6;
+ } else
+#endif
+ if (address.protocol() == QAbstractSocket::IPv4Protocol
+ || address.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) {
+ memset(sockAddrIPv4, 0, sizeof(sockaddr_in));
+ sockAddrIPv4->sin_family = AF_INET;
+ WSAHtons(socketDescriptor, port, &(sockAddrIPv4->sin_port));
+ WSAHtonl(socketDescriptor, address.toIPv4Address(), &(sockAddrIPv4->sin_addr.s_addr));
+ *sockAddrSize = sizeof(sockaddr_in);
+ *sockAddrPtr = (struct sockaddr *) sockAddrIPv4;
+ } else {
+ // unreachable
+ }
+}
+
+/*! \internal
+
+*/
+static inline QAbstractSocket::SocketType qt_socket_getType(int socketDescriptor)
+{
+ int value = 0;
+ QT_SOCKLEN_T valueSize = sizeof(value);
+ if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_TYPE, (char *) &value, &valueSize) != 0) {
+ WS_ERROR_DEBUG(WSAGetLastError());
+ } else {
+ if (value == SOCK_STREAM)
+ return QAbstractSocket::TcpSocket;
+ else if (value == SOCK_DGRAM)
+ return QAbstractSocket::UdpSocket;
+ }
+ return QAbstractSocket::UnknownSocketType;
+}
+
+/*! \internal
+
+*/
+static inline int qt_socket_getMaxMsgSize(int socketDescriptor)
+{
+ int value = 0;
+ QT_SOCKLEN_T valueSize = sizeof(value);
+ if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *) &value, &valueSize) != 0) {
+ WS_ERROR_DEBUG(WSAGetLastError());
+ }
+ return value;
+}
+
+QWindowsSockInit::QWindowsSockInit()
+: version(0)
+{
+ //### should we try for 2.2 on all platforms ??
+ WSAData wsadata;
+
+ // IPv6 requires Winsock v2.0 or better.
+ if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
+ qWarning("QTcpSocketAPI: WinSock v2.0 initialization failed.");
+ } else {
+ version = 0x20;
+ }
+}
+
+QWindowsSockInit::~QWindowsSockInit()
+{
+ WSACleanup();
+}
+
+// MS Transport Provider IOCTL to control
+// reporting PORT_UNREACHABLE messages
+// on UDP sockets via recv/WSARecv/etc.
+// Path TRUE in input buffer to enable (default if supported),
+// FALSE to disable.
+#ifndef SIO_UDP_CONNRESET
+# ifndef IOC_VENDOR
+# define IOC_VENDOR 0x18000000
+# endif
+# ifndef _WSAIOW
+# define _WSAIOW(x,y) (IOC_IN|(x)|(y))
+# endif
+# define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
+#endif
+
+bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol socketProtocol)
+{
+
+ //### no ip6 support on winsocket 1.1 but we will try not to use this !!!!!!!!!!!!1
+ /*
+ if (winsockVersion < 0x20 && socketProtocol == QAbstractSocket::IPv6Protocol) {
+ //### no ip6 support
+ return -1;
+ }
+ */
+
+ int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol) ? AF_INET6 : AF_INET;
+ int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
+ // MSDN KB179942 states that on winnt 4 WSA_FLAG_OVERLAPPED is needed if socket is to be non blocking
+ // and recomends alwasy doing it for cross windows version comapablity.
+ SOCKET socket = ::WSASocket(protocol, type, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
+
+ if (socket == INVALID_SOCKET) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ switch (err) {
+ case WSANOTINITIALISED:
+ //###
+ break;
+ case WSAEAFNOSUPPORT:
+ case WSAESOCKTNOSUPPORT:
+ case WSAEPROTOTYPE:
+ case WSAEINVAL:
+ setError(QAbstractSocket::UnsupportedSocketOperationError, ProtocolUnsupportedErrorString);
+ break;
+ case WSAEMFILE:
+ case WSAENOBUFS:
+ setError(QAbstractSocket::SocketResourceError, ResourceErrorString);
+ break;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+#if !defined(Q_OS_WINCE)
+ 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
+
+ socketDescriptor = socket;
+ return true;
+
+}
+
+/*! \internal
+
+ Returns the value of the socket option \a opt.
+*/
+int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) const
+{
+ Q_Q(const QNativeSocketEngine);
+ if (!q->isValid())
+ return -1;
+
+ int n = -1;
+ int level = SOL_SOCKET; // default
+
+ switch (opt) {
+ case QNativeSocketEngine::ReceiveBufferSocketOption:
+ n = SO_RCVBUF;
+ break;
+ case QNativeSocketEngine::SendBufferSocketOption:
+ n = SO_SNDBUF;
+ break;
+ case QNativeSocketEngine::BroadcastSocketOption:
+ n = SO_BROADCAST;
+ break;
+ case QNativeSocketEngine::NonBlockingSocketOption: {
+ unsigned long buf = 0;
+ if (WSAIoctl(socketDescriptor, FIONBIO, 0,0, &buf, sizeof(buf), 0,0,0) == 0)
+ return buf;
+ else
+ return -1;
+ break;
+ }
+ case QNativeSocketEngine::AddressReusable:
+ n = SO_REUSEADDR;
+ break;
+ case QNativeSocketEngine::BindExclusively:
+ n = SO_EXCLUSIVEADDRUSE;
+ break;
+ case QNativeSocketEngine::ReceiveOutOfBandData:
+ n = SO_OOBINLINE;
+ break;
+ case QNativeSocketEngine::LowDelayOption:
+ level = IPPROTO_TCP;
+ n = TCP_NODELAY;
+ break;
+ 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;
+ QT_SOCKOPTLEN_T len = sizeof(v);
+ if (getsockopt(socketDescriptor, level, n, (char *) &v, &len) != -1)
+ return v;
+ return -1;
+}
+
+
+/*! \internal
+ Sets the socket option \a opt to \a v.
+*/
+bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt, int v)
+{
+ Q_Q(const QNativeSocketEngine);
+ if (!q->isValid())
+ return false;
+
+ int n = 0;
+ int level = SOL_SOCKET; // default
+
+ switch (opt) {
+ case QNativeSocketEngine::ReceiveBufferSocketOption:
+ n = SO_RCVBUF;
+ break;
+ case QNativeSocketEngine::SendBufferSocketOption:
+ n = SO_SNDBUF;
+ break;
+ case QNativeSocketEngine::BroadcastSocketOption:
+ n = SO_BROADCAST;
+ break;
+ case QNativeSocketEngine::NonBlockingSocketOption:
+ {
+ unsigned long buf = v;
+ unsigned long outBuf;
+ DWORD sizeWritten = 0;
+ if (::WSAIoctl(socketDescriptor, FIONBIO, &buf, sizeof(unsigned long), &outBuf, sizeof(unsigned long), &sizeWritten, 0,0) == SOCKET_ERROR) {
+ WS_ERROR_DEBUG(WSAGetLastError());
+ return false;
+ }
+ return true;
+ break;
+ }
+ case QNativeSocketEngine::AddressReusable:
+ n = SO_REUSEADDR;
+ break;
+ case QNativeSocketEngine::BindExclusively:
+ n = SO_EXCLUSIVEADDRUSE;
+ break;
+ case QNativeSocketEngine::ReceiveOutOfBandData:
+ n = SO_OOBINLINE;
+ break;
+ case QNativeSocketEngine::LowDelayOption:
+ level = IPPROTO_TCP;
+ n = TCP_NODELAY;
+ break;
+ 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) {
+ WS_ERROR_DEBUG(WSAGetLastError());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Fetches information about both ends of the connection: whatever is
+ available.
+*/
+bool QNativeSocketEnginePrivate::fetchConnectionParameters()
+{
+ localPort = 0;
+ localAddress.clear();
+ peerPort = 0;
+ peerAddress.clear();
+
+ if (socketDescriptor == -1)
+ return false;
+
+ qt_sockaddr sa;
+ QT_SOCKLEN_T sockAddrSize = sizeof(sa);
+
+ // Determine local address
+ memset(&sa, 0, sizeof(sa));
+ if (::getsockname(socketDescriptor, &sa.a, &sockAddrSize) == 0) {
+ qt_socket_getPortAndAddress(socketDescriptor, &sa, &localPort, &localAddress);
+ // Determine protocol family
+ switch (sa.a.sa_family) {
+ case AF_INET:
+ socketProtocol = QAbstractSocket::IPv4Protocol;
+ break;
+#if !defined (QT_NO_IPV6)
+ case AF_INET6:
+ socketProtocol = QAbstractSocket::IPv6Protocol;
+ break;
+#endif
+ default:
+ socketProtocol = QAbstractSocket::UnknownNetworkLayerProtocol;
+ break;
+ }
+ } else {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ if (err == WSAENOTSOCK) {
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ InvalidSocketErrorString);
+ return false;
+ }
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ if (::getpeername(socketDescriptor, &sa.a, &sockAddrSize) == 0) {
+ qt_socket_getPortAndAddress(socketDescriptor, &sa, &peerPort, &peerAddress);
+ } else {
+ WS_ERROR_DEBUG(WSAGetLastError());
+ }
+
+ socketType = qt_socket_getType(socketDescriptor);
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ QString socketProtocolStr = "UnknownProtocol";
+ if (socketProtocol == QAbstractSocket::IPv4Protocol) socketProtocolStr = "IPv4Protocol";
+ else if (socketProtocol == QAbstractSocket::IPv6Protocol) socketProtocolStr = "IPv6Protocol";
+
+ QString socketTypeStr = "UnknownSocketType";
+ if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = "TcpSocket";
+ else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = "UdpSocket";
+
+ qDebug("QNativeSocketEnginePrivate::fetchConnectionParameters() localAddress == %s, localPort = %i, peerAddress == %s, peerPort = %i, socketProtocol == %s, socketType == %s", localAddress.toString().toLatin1().constData(), localPort, peerAddress.toString().toLatin1().constData(), peerPort, socketProtocolStr.toLatin1().constData(), socketTypeStr.toLatin1().constData());
+#endif
+
+ return true;
+}
+
+
+bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quint16 port)
+{
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeConnect() to %s :: %i", address.toString().toLatin1().constData(), port);
+#endif
+
+ struct sockaddr_in sockAddrIPv4;
+ qt_sockaddr_in6 sockAddrIPv6;
+ struct sockaddr *sockAddrPtr = 0;
+ QT_SOCKLEN_T sockAddrSize = 0;
+
+ qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize);
+
+ forever {
+ int connectResult = ::WSAConnect(socketDescriptor, sockAddrPtr, sockAddrSize, 0,0,0,0);
+ if (connectResult == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+
+ switch (err) {
+ case WSANOTINITIALISED:
+ //###
+ break;
+ case WSAEISCONN:
+ socketState = QAbstractSocket::ConnectedState;
+ break;
+ case WSAEWOULDBLOCK: {
+ // If WSAConnect returns WSAEWOULDBLOCK on the second
+ // connection attempt, we have to check SO_ERROR's
+ // value to detect ECONNREFUSED. If we don't get
+ // ECONNREFUSED, we'll have to treat it as an
+ // unfinished operation.
+ int value = 0;
+ QT_SOCKLEN_T valueSize = sizeof(value);
+ if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
+ if (value == WSAECONNREFUSED) {
+ setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+ if (value == WSAETIMEDOUT) {
+ setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+ if (value == WSAEHOSTUNREACH) {
+ setError(QAbstractSocket::NetworkError, HostUnreachableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+ if (value == WSAEADDRNOTAVAIL) {
+ setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ }
+ }
+ // fall through
+ }
+ case WSAEINPROGRESS:
+ setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
+ socketState = QAbstractSocket::ConnectingState;
+ break;
+ case WSAEADDRINUSE:
+ setError(QAbstractSocket::NetworkError, AddressInuseErrorString);
+ break;
+ case WSAECONNREFUSED:
+ setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAETIMEDOUT:
+ setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString);
+ break;
+ case WSAEACCES:
+ setError(QAbstractSocket::SocketAccessError, AccessErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAEHOSTUNREACH:
+ setError(QAbstractSocket::NetworkError, HostUnreachableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAENETUNREACH:
+ setError(QAbstractSocket::NetworkError, NetworkUnreachableErrorString);
+ socketState = QAbstractSocket::UnconnectedState;
+ break;
+ case WSAEINVAL:
+ case WSAEALREADY:
+ setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
+ break;
+ default:
+ break;
+ }
+ if (socketState != QAbstractSocket::ConnectedState) {
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeConnect(%s, %i) == false (%s)",
+ address.toString().toLatin1().constData(), port,
+ socketState == QAbstractSocket::ConnectingState
+ ? "Connection in progress" : socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+ }
+ break;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeConnect(%s, %i) == true",
+ address.toString().toLatin1().constData(), port);
+#endif
+
+ socketState = QAbstractSocket::ConnectedState;
+ return true;
+}
+
+
+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;
+ QT_SOCKLEN_T sockAddrSize = 0;
+
+ qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize);
+
+
+ int bindResult = ::bind(socketDescriptor, sockAddrPtr, sockAddrSize);
+ if (bindResult == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ switch (err) {
+ case WSANOTINITIALISED:
+ //###
+ break;
+ case WSAEADDRINUSE:
+ case WSAEINVAL:
+ setError(QAbstractSocket::AddressInUseError, AddressInuseErrorString);
+ break;
+ case WSAEACCES:
+ setError(QAbstractSocket::SocketAccessError, AddressProtectedErrorString);
+ break;
+ case WSAEADDRNOTAVAIL:
+ setError(QAbstractSocket::SocketAddressNotAvailableError, AddressNotAvailableErrorString);
+ break;
+ default:
+ break;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeBind(%s, %i) == false (%s)",
+ address.toString().toLatin1().constData(), port, socketErrorString.toLatin1().constData());
+#endif
+
+ return false;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeBind(%s, %i) == true",
+ address.toString().toLatin1().constData(), port);
+#endif
+ socketState = QAbstractSocket::BoundState;
+ return true;
+}
+
+
+bool QNativeSocketEnginePrivate::nativeListen(int backlog)
+{
+ if (::listen(socketDescriptor, backlog) == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ switch (err) {
+ case WSANOTINITIALISED:
+ //###
+ break;
+ case WSAEADDRINUSE:
+ setError(QAbstractSocket::AddressInUseError,
+ PortInuseErrorString);
+ break;
+ default:
+ break;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeListen(%i) == false (%s)",
+ backlog, socketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeListen(%i) == true", backlog);
+#endif
+
+ socketState = QAbstractSocket::ListeningState;
+ return true;
+}
+
+int QNativeSocketEnginePrivate::nativeAccept()
+{
+ int acceptedDescriptor = WSAAccept(socketDescriptor, 0,0,0,0);
+ if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) {
+ // Because of WSAAsyncSelect() WSAAccept returns a non blocking socket
+ // with the same attributes as the listening socket including the current
+ // WSAAsyncSelect(). To be able to change the socket to blocking mode the
+ // WSAAsyncSelect() call must be cancled.
+ QSocketNotifier n(acceptedDescriptor, QSocketNotifier::Read);
+ n.setEnabled(true);
+ n.setEnabled(false);
+ }
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor);
+#endif
+ 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
+{
+ unsigned long nbytes = 0;
+ unsigned long dummy = 0;
+ DWORD sizeWritten = 0;
+ if (::WSAIoctl(socketDescriptor, FIONREAD, &dummy, sizeof(dummy), &nbytes, sizeof(nbytes), &sizeWritten, 0,0) == SOCKET_ERROR) {
+ WS_ERROR_DEBUG(WSAGetLastError());
+ return -1;
+ }
+
+ // ioctlsocket sometimes reports 1 byte available for datagrams
+ // while the following recvfrom returns -1 and claims connection
+ // was reset (udp is connectionless). so we peek one byte to
+ // catch this case and return 0 bytes available if recvfrom
+ // fails.
+ if (nbytes == 1 && socketType == QAbstractSocket::UdpSocket) {
+ char c;
+ WSABUF buf;
+ buf.buf = &c;
+ buf.len = sizeof(c);
+ DWORD flags = MSG_PEEK;
+ if (::WSARecvFrom(socketDescriptor, &buf, 1, 0, &flags, 0,0,0,0) == SOCKET_ERROR)
+ return 0;
+ }
+ return nbytes;
+}
+
+
+bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
+{
+#if !defined(Q_OS_WINCE)
+ // Create a sockaddr struct and reset its port number.
+ qt_sockaddr storage;
+ QT_SOCKLEN_T storageSize = sizeof(storage);
+ memset(&storage, 0, storageSize);
+
+ bool result = false;
+
+ // Peek 0 bytes into the next message. The size of the message may
+ // well be 0, so we check if there was a sender.
+ char c;
+ WSABUF buf;
+ buf.buf = &c;
+ buf.len = sizeof(c);
+ DWORD available = 0;
+ DWORD flags = MSG_PEEK;
+ int ret = ::WSARecvFrom(socketDescriptor, &buf, 1, &available, &flags, &storage.a, &storageSize,0,0);
+ int err = WSAGetLastError();
+ if (ret == SOCKET_ERROR && err != WSAEMSGSIZE) {
+ WS_ERROR_DEBUG(err);
+ if (err == WSAECONNRESET) {
+ // Discard error message to prevent QAbstractSocket from
+ // getting this message repeatedly after reenabling the
+ // notifiers.
+ flags = 0;
+ ::WSARecvFrom(socketDescriptor, &buf, 1, &available, &flags,
+ &storage.a, &storageSize, 0, 0);
+ }
+ } else {
+ // If there's no error, or if our buffer was too small, there must be
+ // a pending datagram.
+ result = true;
+ }
+
+#else // Q_OS_WINCE
+ bool result = false;
+ fd_set readS;
+ FD_ZERO(&readS);
+ FD_SET((SOCKET)socketDescriptor, &readS);
+ timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 5000;
+ int available = ::select(1, &readS, 0, 0, &timeout);
+ result = available > 0 ? true : false;
+#endif
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeHasPendingDatagrams() == %s",
+ result ? "true" : "false");
+#endif
+ return result;
+}
+
+
+qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
+{
+ qint64 ret = -1;
+#if !defined(Q_OS_WINCE)
+ int recvResult = 0;
+ DWORD flags;
+ DWORD bufferCount = 5;
+ WSABUF * buf = 0;
+ for (;;) {
+ // the data written to udpMessagePeekBuffer is discarded, so
+ // this function is still reentrant although it might not look
+ // so.
+ static char udpMessagePeekBuffer[8192];
+
+ buf = new WSABUF[bufferCount];
+ for (DWORD i=0; i<bufferCount; i++) {
+ buf[i].buf = udpMessagePeekBuffer;
+ buf[i].len = sizeof(udpMessagePeekBuffer);
+ }
+ flags = MSG_PEEK;
+ DWORD bytesRead = 0;
+ recvResult = ::WSARecv(socketDescriptor, buf, bufferCount, &bytesRead, &flags, 0,0);
+ int err = WSAGetLastError();
+ if (recvResult != SOCKET_ERROR) {
+ ret = qint64(bytesRead);
+ break;
+ } else if (recvResult == SOCKET_ERROR && err == WSAEMSGSIZE) {
+ bufferCount += 5;
+ delete[] buf;
+ } else if (recvResult == SOCKET_ERROR) {
+ WS_ERROR_DEBUG(err);
+ ret = -1;
+ break;
+ }
+ }
+
+ if (buf)
+ delete[] buf;
+
+#else // Q_OS_WINCE
+ DWORD size = -1;
+ DWORD bytesReturned;
+ int ioResult = WSAIoctl(socketDescriptor, FIONREAD, 0,0, &size, sizeof(size), &bytesReturned, 0, 0);
+ if (ioResult == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ } else {
+ ret = qint64(size);
+ }
+#endif
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %li", ret);
+#endif
+
+ return ret;
+}
+
+
+qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength,
+ QHostAddress *address, quint16 *port)
+{
+ qint64 ret = 0;
+
+ qt_sockaddr aa;
+ memset(&aa, 0, sizeof(aa));
+ QT_SOCKLEN_T sz;
+ sz = sizeof(aa);
+
+ WSABUF buf;
+ buf.buf = data;
+ buf.len = maxLength;
+#if !defined(Q_OS_WINCE)
+ buf.buf = data;
+ buf.len = maxLength;
+#else
+ char tmpChar;
+ buf.buf = data ? data : &tmpChar;
+ buf.len = maxLength;
+#endif
+
+ DWORD flags = 0;
+ DWORD bytesRead = 0;
+ int wsaRet = ::WSARecvFrom(socketDescriptor, &buf, 1, &bytesRead, &flags, &aa.a, &sz,0,0);
+ if (wsaRet == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ if (err == WSAEMSGSIZE) {
+ // it is ok the buffer was to small if bytesRead is larger than
+ // maxLength then assume bytes read is really maxLenth
+ ret = qint64(bytesRead) > maxLength ? maxLength : qint64(bytesRead);
+ } else {
+ WS_ERROR_DEBUG(err);
+ setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
+ ret = -1;
+ }
+ } else {
+ ret = qint64(bytesRead);
+ }
+
+ qt_socket_getPortAndAddress(socketDescriptor, &aa, port, address);
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeReceiveDatagram(%p \"%s\", %li, %s, %i) == %li",
+ data, qt_prettyDebug(data, qMin<qint64>(ret, 16), ret).data(), maxLength,
+ address ? address->toString().toLatin1().constData() : "(nil)",
+ port ? *port : 0, ret);
+#endif
+
+ return ret;
+}
+
+
+qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len,
+ const QHostAddress &address, quint16 port)
+{
+ qint64 ret = -1;
+ struct sockaddr_in sockAddrIPv4;
+ qt_sockaddr_in6 sockAddrIPv6;
+ struct sockaddr *sockAddrPtr = 0;
+ QT_SOCKLEN_T sockAddrSize = 0;
+
+ qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize);
+
+ WSABUF buf;
+#if !defined(Q_OS_WINCE)
+ buf.buf = len ? (char*)data : 0;
+#else
+ char tmp;
+ buf.buf = len ? (char*)data : &tmp;
+#endif
+ buf.len = len;
+ DWORD flags = 0;
+ DWORD bytesSent = 0;
+ if (::WSASendTo(socketDescriptor, &buf, 1, &bytesSent, flags, sockAddrPtr, sockAddrSize, 0,0) == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ switch (err) {
+ case WSAEMSGSIZE:
+ setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString);
+ break;
+ default:
+ setError(QAbstractSocket::NetworkError, SendDatagramErrorString);
+ break;
+ }
+ ret = -1;
+ } else {
+ ret = qint64(bytesSent);
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeSendDatagram(%p \"%s\", %li, \"%s\", %i) == %li", data,
+ qt_prettyDebug(data, qMin<qint64>(len, 16), len).data(), 0, address.toString().toLatin1().constData(),
+ port, ret);
+#endif
+
+ return ret;
+}
+
+
+qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
+{
+ Q_Q(QNativeSocketEngine);
+ qint64 ret = 0;
+ qint64 bytesToSend = len;
+
+ for (;;) {
+ WSABUF buf;
+ buf.buf = (char*)data + ret;
+ buf.len = bytesToSend;
+ DWORD flags = 0;
+ DWORD bytesWritten = 0;
+
+ int socketRet = ::WSASend(socketDescriptor, &buf, 1, &bytesWritten, flags, 0,0);
+
+ ret += qint64(bytesWritten);
+
+ int err;
+ if (socketRet != SOCKET_ERROR) {
+ if (ret == len)
+ break;
+ else
+ continue;
+ } else if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
+ break;
+ } else if (err == WSAENOBUFS) {
+ // this function used to not send more than 49152 per call to WSASendTo
+ // to avoid getting a WSAENOBUFS. However this is a performance regression
+ // and we think it only appears with old windows versions. We now handle the
+ // WSAENOBUFS and hope it never appears anyway.
+ // just go on, the next loop run we will try a smaller number
+ } else {
+ WS_ERROR_DEBUG(err);
+ switch (err) {
+ case WSAECONNRESET:
+ case WSAECONNABORTED:
+ ret = -1;
+ setError(QAbstractSocket::NetworkError, WriteErrorString);
+ q->close();
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ // for next send:
+ bytesToSend = qMin<qint64>(49152, len - ret);
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeWrite(%p \"%s\", %li) == %li",
+ data, qt_prettyDebug(data, qMin((int)ret, 16), (int)ret).data(), (int)len, (int)ret);
+#endif
+
+ return ret;
+}
+
+qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
+{
+ qint64 ret = -1;
+ WSABUF buf;
+ buf.buf = data;
+ buf.len = maxLength;
+ DWORD flags = 0;
+ DWORD bytesRead = 0;
+#if defined(Q_OS_WINCE)
+ WSASetLastError(0);
+#endif
+ if (::WSARecv(socketDescriptor, &buf, 1, &bytesRead, &flags, 0,0) == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+ WS_ERROR_DEBUG(err);
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ ret = -2;
+ break;
+ case WSAEBADF:
+ case WSAEINVAL:
+ //error string is now set in read(), not here in nativeRead()
+ break;
+ case WSAECONNRESET:
+ case WSAECONNABORTED:
+ // for tcp sockets this will be handled in QNativeSocketEngine::read
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ ret = -2;
+ else
+ ret = qint64(bytesRead);
+ }
+
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ if (ret != -2) {
+ qDebug("QNativeSocketEnginePrivate::nativeRead(%p \"%s\", %l) == %li",
+ data, qt_prettyDebug(data, qMin((int)bytesRead, 16), (int)bytesRead).data(), (int)maxLength, (int)ret);
+ } else {
+ qDebug("QNativeSocketEnginePrivate::nativeRead(%p, %l) == -2 (WOULD BLOCK)",
+ data, int(maxLength));
+ }
+#endif
+
+ return ret;
+}
+
+int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+{
+ bool readEnabled = selectForRead && readNotifier && readNotifier->isEnabled();
+ if (readEnabled)
+ readNotifier->setEnabled(false);
+
+ fd_set fds;
+
+ int ret = 0;
+
+ memset(&fds, 0, sizeof(fd_set));
+ fds.fd_count = 1;
+ fds.fd_array[0] = (SOCKET)socketDescriptor;
+
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ if (selectForRead) {
+ ret = select(0, &fds, 0, 0, timeout < 0 ? 0 : &tv);
+ } else {
+ // select for write
+
+ // Windows needs this to report errors when connecting a socket ...
+ fd_set fdexception;
+ FD_ZERO(&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((SOCKET)socketDescriptor, &fdexception))
+ ret--;
+ }
+
+ if (readEnabled)
+ readNotifier->setEnabled(true);
+
+ return ret;
+}
+
+int QNativeSocketEnginePrivate::nativeSelect(int timeout,
+ bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const
+{
+ bool readEnabled = checkRead && readNotifier && readNotifier->isEnabled();
+ if (readEnabled)
+ readNotifier->setEnabled(false);
+
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexception;
+
+ int ret = 0;
+
+ memset(&fdread, 0, sizeof(fd_set));
+ if (checkRead) {
+ fdread.fd_count = 1;
+ 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] = (SOCKET)socketDescriptor;
+
+ // Windows needs this to report errors when connecting a socket
+ FD_SET((SOCKET)socketDescriptor, &fdexception);
+ }
+
+ struct timeval tv;
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+#if !defined(Q_OS_WINCE)
+ ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
+#else
+ ret = select(1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
+#endif
+
+ //... but if it is actually set, pretend it did not happen
+ if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
+ ret--;
+
+ if (readEnabled)
+ readNotifier->setEnabled(true);
+
+ if (ret <= 0)
+ return ret;
+
+ *selectForRead = FD_ISSET((SOCKET)socketDescriptor, &fdread);
+ *selectForWrite = FD_ISSET((SOCKET)socketDescriptor, &fdwrite);
+
+ return ret;
+}
+
+void QNativeSocketEnginePrivate::nativeClose()
+{
+#if defined (QTCPSOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::nativeClose()");
+#endif
+ // We were doing a setsockopt here before with SO_DONTLINGER. (However with kind of wrong
+ // usage of parameters, it wants a BOOL but we used a struct and pretended it to be bool).
+ // We don't think setting this option should be done here, if a user wants it she/he can
+ // do it manually with socketDescriptor()/setSocketDescriptor();
+ ::closesocket(socketDescriptor);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h
new file mode 100644
index 0000000000..c406ed948c
--- /dev/null
+++ b/src/network/socket/qnet_unix_p.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNET_UNIX_P_H
+#define QNET_UNIX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of Qt code on Unix. This header file may change from version to
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qcore_unix_p.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#if defined(Q_OS_VXWORKS)
+# include <sockLib.h>
+#endif
+
+// for inet_addr
+#include <netdb.h>
+#include <arpa/inet.h>
+#if defined(Q_OS_VXWORKS)
+# include <hostLib.h>
+#else
+# include <resolv.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Almost always the same. If not, specify in qplatformdefs.h.
+#if !defined(QT_SOCKOPTLEN_T)
+# define QT_SOCKOPTLEN_T QT_SOCKLEN_T
+#endif
+
+// UnixWare 7 redefines socket -> _socket
+static inline int qt_safe_socket(int domain, int type, int protocol, int flags = 0)
+{
+ Q_ASSERT((flags & ~O_NONBLOCK) == 0);
+
+ register int fd;
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ int newtype = type | SOCK_CLOEXEC;
+ if (flags & O_NONBLOCK)
+ newtype |= SOCK_NONBLOCK;
+ fd = ::socket(domain, newtype, protocol);
+ if (fd != -1 || errno != EINVAL)
+ return fd;
+#endif
+
+ fd = ::socket(domain, type, protocol);
+ if (fd == -1)
+ return -1;
+
+ ::fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ // set non-block too?
+ if (flags & O_NONBLOCK)
+ ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+ return fd;
+}
+
+// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED
+static inline int qt_safe_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *addrlen, int flags = 0)
+{
+ Q_ASSERT((flags & ~O_NONBLOCK) == 0);
+
+ register int fd;
+#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ // use accept4
+ int sockflags = SOCK_CLOEXEC;
+ if (flags & O_NONBLOCK)
+ sockflags |= SOCK_NONBLOCK;
+ fd = ::accept4(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen), sockflags);
+ if (fd != -1 || !(errno == ENOSYS || errno == EINVAL))
+ return fd;
+#endif
+
+ fd = ::accept(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen));
+ if (fd == -1)
+ return -1;
+
+ ::fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ // set non-block too?
+ if (flags & O_NONBLOCK)
+ ::fcntl(fd, F_SETFL, ::fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+ return fd;
+}
+
+// UnixWare 7 redefines listen -> _listen
+static inline int qt_safe_listen(int s, int backlog)
+{
+ return ::listen(s, backlog);
+}
+
+static inline int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SOCKLEN_T addrlen)
+{
+ register int ret;
+ // Solaris e.g. expects a non-const 2nd parameter
+ EINTR_LOOP(ret, QT_SOCKET_CONNECT(sockfd, const_cast<struct sockaddr *>(addr), addrlen));
+ return ret;
+}
+#undef QT_SOCKET_CONNECT
+#define QT_SOCKET_CONNECT qt_safe_connect
+
+#if defined(socket)
+# undef socket
+#endif
+#if defined(accept)
+# undef accept
+#endif
+#if defined(listen)
+# undef listen
+#endif
+
+// VxWorks' headers specify 'int' instead of '...' for the 3rd ioctl() parameter.
+template <typename T>
+static inline int qt_safe_ioctl(int sockfd, int request, T arg)
+{
+#ifdef Q_OS_VXWORKS
+ return ::ioctl(sockfd, request, (int) arg);
+#else
+ return ::ioctl(sockfd, request, arg);
+#endif
+}
+
+// VxWorks' headers do not specify any const modifiers
+static inline in_addr_t qt_safe_inet_addr(const char *cp)
+{
+#ifdef Q_OS_VXWORKS
+ return ::inet_addr((char *) cp);
+#else
+ return ::inet_addr(cp);
+#endif
+}
+
+// VxWorks' headers do not specify any const modifiers
+static inline int qt_safe_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *to, QT_SOCKLEN_T tolen)
+{
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+
+ register int ret;
+#ifdef Q_OS_VXWORKS
+ EINTR_LOOP(ret, ::sendto(sockfd, (char *) buf, len, flags, (struct sockaddr *) to, tolen));
+#else
+ EINTR_LOOP(ret, ::sendto(sockfd, buf, len, flags, to, tolen));
+#endif
+ return ret;
+}
+
+QT_END_NAMESPACE
+
+#endif // QNET_UNIX_P_H
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
new file mode 100644
index 0000000000..c365635990
--- /dev/null
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -0,0 +1,1923 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsocks5socketengine_p.h"
+
+#ifndef QT_NO_SOCKS5
+
+#include "qtcpsocket.h"
+#include "qudpsocket.h"
+#include "qtcpserver.h"
+#include "qdebug.h"
+#include "qhash.h"
+#include "qqueue.h"
+#include "qelapsedtimer.h"
+#include "qmutex.h"
+#include "qthread.h"
+#include "qcoreapplication.h"
+#include "qurl.h"
+#include "qauthenticator.h"
+#include <qendian.h>
+#include <qnetworkinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_SYMBIAN
+static const int MaxWriteBufferSize = 4*1024;
+#else
+static const int MaxWriteBufferSize = 128*1024;
+#endif
+
+//#define QSOCKS5SOCKETLAYER_DEBUG
+
+#define MAX_DATA_DUMP 256
+#if !defined(Q_OS_WINCE)
+#define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
+#else
+#define SOCKS5_BLOCKING_BIND_TIMEOUT 10000
+#endif
+
+#define Q_INIT_CHECK(returnValue) do { \
+ if (!d->data) { \
+ return returnValue; \
+ } } while (0)
+
+#define S5_VERSION_5 0x05
+#define S5_CONNECT 0x01
+#define S5_BIND 0x02
+#define S5_UDP_ASSOCIATE 0x03
+#define S5_IP_V4 0x01
+#define S5_DOMAINNAME 0x03
+#define S5_IP_V6 0x04
+#define S5_SUCCESS 0x00
+#define S5_R_ERROR_SOCKS_FAILURE 0x01
+#define S5_R_ERROR_CON_NOT_ALLOWED 0x02
+#define S5_R_ERROR_NET_UNREACH 0x03
+#define S5_R_ERROR_HOST_UNREACH 0x04
+#define S5_R_ERROR_CONN_REFUSED 0x05
+#define S5_R_ERROR_TTL 0x06
+#define S5_R_ERROR_CMD_NOT_SUPPORTED 0x07
+#define S5_R_ERROR_ADD_TYPE_NOT_SUPORTED 0x08
+
+#define S5_AUTHMETHOD_NONE 0x00
+#define S5_AUTHMETHOD_PASSWORD 0x02
+#define S5_AUTHMETHOD_NOTACCEPTABLE 0xFF
+
+#define S5_PASSWORDAUTH_VERSION 0x01
+
+#ifdef QSOCKS5SOCKETLAYER_DEBUG
+# define QSOCKS5_Q_DEBUG qDebug() << this
+# define QSOCKS5_D_DEBUG qDebug() << q_ptr
+# define QSOCKS5_DEBUG qDebug() << "[QSocks5]"
+static QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State s)
+{
+ switch (s) {
+ case QSocks5SocketEnginePrivate::Uninitialized: return QLatin1String("Uninitialized");
+ case QSocks5SocketEnginePrivate::ConnectError: return QLatin1String("ConnectError");
+ case QSocks5SocketEnginePrivate::AuthenticationMethodsSent: return QLatin1String("AuthenticationMethodsSent");
+ case QSocks5SocketEnginePrivate::Authenticating: return QLatin1String("Authenticating");
+ case QSocks5SocketEnginePrivate::AuthenticatingError: return QLatin1String("AuthenticatingError");
+ case QSocks5SocketEnginePrivate::RequestMethodSent: return QLatin1String("RequestMethodSent");
+ case QSocks5SocketEnginePrivate::RequestError: return QLatin1String("RequestError");
+ case QSocks5SocketEnginePrivate::Connected: return QLatin1String("Connected");
+ case QSocks5SocketEnginePrivate::UdpAssociateSuccess: return QLatin1String("UdpAssociateSuccess");
+ case QSocks5SocketEnginePrivate::BindSuccess: return QLatin1String("BindSuccess");
+ case QSocks5SocketEnginePrivate::ControlSocketError: return QLatin1String("ControlSocketError");
+ case QSocks5SocketEnginePrivate::SocksError: return QLatin1String("SocksError");
+ case QSocks5SocketEnginePrivate::HostNameLookupError: return QLatin1String("HostNameLookupError");
+ default: break;
+ }
+ return QLatin1String("unknown state");
+}
+
+static QString dump(const QByteArray &buf)
+{
+ QString data;
+ for (int i = 0; i < qMin<int>(MAX_DATA_DUMP, buf.size()); ++i) {
+ if (i) data += QLatin1Char(' ');
+ uint val = (unsigned char)buf.at(i);
+ // data += QString("0x%1").arg(val, 3, 16, QLatin1Char('0'));
+ data += QString::number(val);
+ }
+ if (buf.size() > MAX_DATA_DUMP)
+ data += QLatin1String(" ...");
+
+ return QString::fromLatin1("size: %1 data: { %2 }").arg(buf.size()).arg(data);
+}
+
+#else
+# define QSOCKS5_DEBUG if (0) qDebug()
+# define QSOCKS5_Q_DEBUG if (0) qDebug()
+# define QSOCKS5_D_DEBUG if (0) qDebug()
+
+static inline QString s5StateToString(QSocks5SocketEnginePrivate::Socks5State) { return QString(); }
+static inline QString dump(const QByteArray &) { return QString(); }
+#endif
+
+/*
+ inserts the host address in buf at pos and updates pos.
+ if the func fails the data in buf and the vallue of pos is undefined
+*/
+static bool qt_socks5_set_host_address_and_port(const QHostAddress &address, quint16 port, QByteArray *pBuf)
+{
+ QSOCKS5_DEBUG << "setting [" << address << ':' << port << ']';
+
+ union {
+ quint16 port;
+ quint32 ipv4;
+ QIPv6Address ipv6;
+ char ptr;
+ } data;
+
+ // add address
+ if (address.protocol() == QAbstractSocket::IPv4Protocol) {
+ data.ipv4 = qToBigEndian<quint32>(address.toIPv4Address());
+ pBuf->append(S5_IP_V4);
+ pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv4));
+ } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
+ data.ipv6 = address.toIPv6Address();
+ pBuf->append(S5_IP_V6);
+ pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.ipv6));
+ } else {
+ return false;
+ }
+
+ // add port
+ data.port = qToBigEndian<quint16>(port);
+ pBuf->append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
+ return true;
+}
+
+/*
+ like above, but for a hostname
+*/
+static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 port, QByteArray *pBuf)
+{
+ QSOCKS5_DEBUG << "setting [" << hostname << ':' << port << ']';
+
+ QByteArray encodedHostName = QUrl::toAce(hostname);
+ QByteArray &buf = *pBuf;
+
+ if (encodedHostName.length() > 255)
+ return false;
+
+ buf.append(S5_DOMAINNAME);
+ buf.append(uchar(encodedHostName.length()));
+ buf.append(encodedHostName);
+
+ // add port
+ union {
+ quint16 port;
+ char ptr;
+ } data;
+ data.port = qToBigEndian<quint16>(port);
+ buf.append(QByteArray::fromRawData(&data.ptr, sizeof data.port));
+
+ return true;
+}
+
+
+/*
+ retrives the host address in buf at pos and updates pos.
+ if the func fails the value of the address and the pos is undefined
+*/
+static bool qt_socks5_get_host_address_and_port(const QByteArray &buf, QHostAddress *pAddress, quint16 *pPort, int *pPos)
+{
+ bool ret = false;
+ int pos = *pPos;
+ const unsigned char *pBuf = reinterpret_cast<const unsigned char*>(buf.constData());
+ QHostAddress address;
+ quint16 port = 0;
+
+ if (buf.size() - pos < 1) {
+ QSOCKS5_DEBUG << "need more data address/port";
+ return false;
+ }
+ if (pBuf[pos] == S5_IP_V4) {
+ pos++;
+ if (buf.size() - pos < 4) {
+ QSOCKS5_DEBUG << "need more data for ip4 address";
+ return false;
+ }
+ address.setAddress(qFromBigEndian<quint32>(&pBuf[pos]));
+ pos += 4;
+ ret = true;
+ } else if (pBuf[pos] == S5_IP_V6) {
+ pos++;
+ if (buf.size() - pos < 16) {
+ QSOCKS5_DEBUG << "need more data for ip6 address";
+ return false;
+ }
+ QIPv6Address add;
+ for (int i = 0; i < 16; ++i)
+ add[i] = buf[pos++];
+ ret = true;
+ } else if (pBuf[pos] == S5_DOMAINNAME){
+ // just skip it
+ pos++;
+ qDebug() << "skipping hostname of len" << uint(pBuf[pos]);
+ pos += uchar(pBuf[pos]);
+ } else {
+ QSOCKS5_DEBUG << "invalid address type" << (int)pBuf[pos];
+ ret = false;
+ }
+
+ if (ret) {
+ if (buf.size() - pos < 2) {
+ QSOCKS5_DEBUG << "need more data for port";
+ return false;
+ }
+ port = qFromBigEndian<quint16>(&pBuf[pos]);
+ pos += 2;
+ }
+
+ if (ret) {
+ QSOCKS5_DEBUG << "got [" << address << ':' << port << ']';
+ *pAddress = address;
+ *pPort = port;
+ *pPos = pos;
+ }
+
+ return ret;
+}
+
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+struct QSocks5Data
+{
+ QTcpSocket *controlSocket;
+ QSocks5Authenticator *authenticator;
+};
+
+struct QSocks5ConnectData : public QSocks5Data
+{
+ QByteArray readBuffer;
+};
+
+struct QSocks5BindData : public QSocks5Data
+{
+ QHostAddress localAddress;
+ quint16 localPort;
+ QHostAddress peerAddress;
+ quint16 peerPort;
+ QElapsedTimer timeStamp;
+};
+
+struct QSocks5RevivedDatagram
+{
+ QByteArray data;
+ QHostAddress address;
+ quint16 port;
+};
+
+#ifndef QT_NO_UDPSOCKET
+struct QSocks5UdpAssociateData : public QSocks5Data
+{
+ QUdpSocket *udpSocket;
+ QHostAddress associateAddress;
+ quint16 associatePort;
+ QQueue<QSocks5RevivedDatagram> pendingDatagrams;
+};
+#endif
+
+// needs to be thread safe
+class QSocks5BindStore : public QObject
+{
+public:
+ QSocks5BindStore();
+ ~QSocks5BindStore();
+
+ void add(int socketDescriptor, QSocks5BindData *bindData);
+ bool contains(int socketDescriptor);
+ QSocks5BindData *retrieve(int socketDescriptor);
+
+protected:
+ void timerEvent(QTimerEvent * event);
+
+ QMutex mutex;
+ int sweepTimerId;
+ //socket descriptor, data, timestamp
+ QHash<int, QSocks5BindData *> store;
+};
+
+Q_GLOBAL_STATIC(QSocks5BindStore, socks5BindStore)
+
+QSocks5BindStore::QSocks5BindStore()
+ : mutex(QMutex::Recursive)
+ , sweepTimerId(-1)
+{
+ QCoreApplication *app = QCoreApplication::instance();
+ if (app && app->thread() != thread())
+ moveToThread(app->thread());
+}
+
+QSocks5BindStore::~QSocks5BindStore()
+{
+}
+
+void QSocks5BindStore::add(int socketDescriptor, QSocks5BindData *bindData)
+{
+ QMutexLocker lock(&mutex);
+ if (store.contains(socketDescriptor)) {
+ // qDebug() << "delete it";
+ }
+ bindData->timeStamp.start();
+ store.insert(socketDescriptor, bindData);
+ // start sweep timer if not started
+ if (sweepTimerId == -1)
+ sweepTimerId = startTimer(60000);
+}
+
+bool QSocks5BindStore::contains(int socketDescriptor)
+{
+ QMutexLocker lock(&mutex);
+ return store.contains(socketDescriptor);
+}
+
+QSocks5BindData *QSocks5BindStore::retrieve(int socketDescriptor)
+{
+ QMutexLocker lock(&mutex);
+ if (!store.contains(socketDescriptor))
+ return 0;
+ QSocks5BindData *bindData = store.take(socketDescriptor);
+ if (bindData) {
+ if (bindData->controlSocket->thread() != QThread::currentThread()) {
+ qWarning("Can not access socks5 bind data from different thread");
+ return 0;
+ }
+ } else {
+ QSOCKS5_DEBUG << "__ERROR__ binddata == 0";
+ }
+ // stop the sweep timer if not needed
+ if (store.isEmpty()) {
+ killTimer(sweepTimerId);
+ sweepTimerId = -1;
+ }
+ return bindData;
+}
+
+void QSocks5BindStore::timerEvent(QTimerEvent * event)
+{
+ QMutexLocker lock(&mutex);
+ if (event->timerId() == sweepTimerId) {
+ QSOCKS5_DEBUG << "QSocks5BindStore performing sweep";
+ QMutableHashIterator<int, QSocks5BindData *> it(store);
+ while (it.hasNext()) {
+ it.next();
+ if (it.value()->timeStamp.hasExpired(350000)) {
+ QSOCKS5_DEBUG << "QSocks5BindStore removing JJJJ";
+ it.remove();
+ }
+ }
+ }
+}
+
+QSocks5Authenticator::QSocks5Authenticator()
+{
+}
+
+QSocks5Authenticator::~QSocks5Authenticator()
+{
+}
+
+char QSocks5Authenticator::methodId()
+{
+ return 0x00;
+}
+
+bool QSocks5Authenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
+{
+ Q_UNUSED(socket);
+ *completed = true;
+ return true;
+}
+
+bool QSocks5Authenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
+{
+ Q_UNUSED(socket);
+ *completed = true;
+ return true;
+}
+
+bool QSocks5Authenticator::seal(const QByteArray buf, QByteArray *sealedBuf)
+{
+ *sealedBuf = buf;
+ return true;
+}
+
+bool QSocks5Authenticator::unSeal(const QByteArray sealedBuf, QByteArray *buf)
+{
+ *buf = sealedBuf;
+ return true;
+}
+
+bool QSocks5Authenticator::unSeal(QTcpSocket *sealedSocket, QByteArray *buf)
+{
+ return unSeal(sealedSocket->readAll(), buf);
+}
+
+QSocks5PasswordAuthenticator::QSocks5PasswordAuthenticator(const QString &userName, const QString &password)
+{
+ this->userName = userName;
+ this->password = password;
+}
+
+char QSocks5PasswordAuthenticator::methodId()
+{
+ return 0x02;
+}
+
+bool QSocks5PasswordAuthenticator::beginAuthenticate(QTcpSocket *socket, bool *completed)
+{
+ *completed = false;
+ QByteArray uname = userName.toLatin1();
+ QByteArray passwd = password.toLatin1();
+ QByteArray dataBuf(3 + uname.size() + passwd.size(), 0);
+ char *buf = dataBuf.data();
+ int pos = 0;
+ buf[pos++] = S5_PASSWORDAUTH_VERSION;
+ buf[pos++] = uname.size();
+ memcpy(&buf[pos], uname.data(), uname.size());
+ pos += uname.size();
+ buf[pos++] = passwd.size();
+ memcpy(&buf[pos], passwd.data(), passwd.size());
+ return socket->write(dataBuf) == dataBuf.size();
+}
+
+bool QSocks5PasswordAuthenticator::continueAuthenticate(QTcpSocket *socket, bool *completed)
+{
+ *completed = false;
+
+ if (socket->bytesAvailable() < 2)
+ return true;
+
+ QByteArray buf = socket->read(2);
+ if (buf.at(0) == S5_PASSWORDAUTH_VERSION && buf.at(1) == 0x00) {
+ *completed = true;
+ return true;
+ }
+
+ // must disconnect
+ socket->close();
+ return false;
+}
+
+QString QSocks5PasswordAuthenticator::errorString()
+{
+ return QLatin1String("Socks5 user name or password incorrect");
+}
+
+
+
+QSocks5SocketEnginePrivate::QSocks5SocketEnginePrivate()
+ : socks5State(Uninitialized)
+ , readNotificationEnabled(false)
+ , writeNotificationEnabled(false)
+ , exceptNotificationEnabled(false)
+ , socketDescriptor(-1)
+ , data(0)
+ , connectData(0)
+#ifndef QT_NO_UDPSOCKET
+ , udpData(0)
+#endif
+ , bindData(0)
+ , readNotificationActivated(false)
+ , writeNotificationActivated(false)
+ , readNotificationPending(false)
+ , writeNotificationPending(false)
+ , connectionNotificationPending(false)
+{
+ mode = NoMode;
+}
+
+QSocks5SocketEnginePrivate::~QSocks5SocketEnginePrivate()
+{
+}
+
+void QSocks5SocketEnginePrivate::initialize(Socks5Mode socks5Mode)
+{
+ Q_Q(QSocks5SocketEngine);
+
+ mode = socks5Mode;
+ if (mode == ConnectMode) {
+ connectData = new QSocks5ConnectData;
+ data = connectData;
+#ifndef QT_NO_UDPSOCKET
+ } else if (mode == UdpAssociateMode) {
+ 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()),
+ Qt::DirectConnection);
+#endif // QT_NO_UDPSOCKET
+ } else if (mode == BindMode) {
+ bindData = new QSocks5BindData;
+ data = bindData;
+ }
+
+ 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);
+ QObject::connect(data->controlSocket, SIGNAL(readyRead()), q, SLOT(_q_controlSocketReadNotification()),
+ Qt::DirectConnection);
+ QObject::connect(data->controlSocket, SIGNAL(bytesWritten(qint64)), q, SLOT(_q_controlSocketBytesWritten()),
+ Qt::DirectConnection);
+ QObject::connect(data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ q, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
+ Qt::DirectConnection);
+ QObject::connect(data->controlSocket, SIGNAL(disconnected()), q, SLOT(_q_controlSocketDisconnected()),
+ Qt::DirectConnection);
+ QObject::connect(data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ q, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
+ Qt::DirectConnection);
+
+ if (!proxyInfo.user().isEmpty() || !proxyInfo.password().isEmpty()) {
+ QSOCKS5_D_DEBUG << "using username/password authentication; user =" << proxyInfo.user();
+ data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
+ } else {
+ QSOCKS5_D_DEBUG << "not using authentication";
+ data->authenticator = new QSocks5Authenticator();
+ }
+}
+
+void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString &extraMessage)
+{
+ Q_Q(QSocks5SocketEngine);
+
+ switch (state) {
+ case Uninitialized:
+ case Authenticating:
+ case AuthenticationMethodsSent:
+ case RequestMethodSent:
+ case Connected:
+ case UdpAssociateSuccess:
+ case BindSuccess:
+ // these aren't error states
+ return;
+
+ case ConnectError:
+ case ControlSocketError: {
+ QAbstractSocket::SocketError controlSocketError = data->controlSocket->error();
+ if (socks5State != Connected) {
+ switch (controlSocketError) {
+ case QAbstractSocket::ConnectionRefusedError:
+ q->setError(QAbstractSocket::ProxyConnectionRefusedError,
+ QSocks5SocketEngine::tr("Connection to proxy refused"));
+ break;
+ case QAbstractSocket::RemoteHostClosedError:
+ q->setError(QAbstractSocket::ProxyConnectionClosedError,
+ QSocks5SocketEngine::tr("Connection to proxy closed prematurely"));
+ break;
+ case QAbstractSocket::HostNotFoundError:
+ q->setError(QAbstractSocket::ProxyNotFoundError,
+ QSocks5SocketEngine::tr("Proxy host not found"));
+ break;
+ case QAbstractSocket::SocketTimeoutError:
+ if (state == ConnectError) {
+ q->setError(QAbstractSocket::ProxyConnectionTimeoutError,
+ QSocks5SocketEngine::tr("Connection to proxy timed out"));
+ break;
+ }
+ /* fall through */
+ default:
+ q->setError(controlSocketError, data->controlSocket->errorString());
+ break;
+ }
+ } else {
+ q->setError(controlSocketError, data->controlSocket->errorString());
+ }
+ break;
+ }
+
+ case AuthenticatingError:
+ q->setError(QAbstractSocket::ProxyAuthenticationRequiredError,
+ extraMessage.isEmpty() ?
+ QSocks5SocketEngine::tr("Proxy authentication failed") :
+ QSocks5SocketEngine::tr("Proxy authentication failed: %1").arg(extraMessage));
+ break;
+
+ case RequestError:
+ // error code set by caller (overload)
+ break;
+
+ case SocksError:
+ q->setError(QAbstractSocket::ProxyProtocolError,
+ QSocks5SocketEngine::tr("SOCKS version 5 protocol error"));
+ break;
+
+ case HostNameLookupError:
+ q->setError(QAbstractSocket::HostNotFoundError,
+ QAbstractSocket::tr("Host not found"));
+ break;
+ }
+
+ q->setState(QAbstractSocket::UnconnectedState);
+ socks5State = state;
+}
+
+void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, Socks5Error socks5error)
+{
+ Q_Q(QSocks5SocketEngine);
+ switch (socks5error) {
+ case SocksFailure:
+ q->setError(QAbstractSocket::NetworkError,
+ QSocks5SocketEngine::tr("General SOCKSv5 server failure"));
+ break;
+ case ConnectionNotAllowed:
+ q->setError(QAbstractSocket::SocketAccessError,
+ QSocks5SocketEngine::tr("Connection not allowed by SOCKSv5 server"));
+ break;
+ case NetworkUnreachable:
+ q->setError(QAbstractSocket::NetworkError,
+ QAbstractSocket::tr("Network unreachable"));
+ break;
+ case HostUnreachable:
+ q->setError(QAbstractSocket::HostNotFoundError,
+ QAbstractSocket::tr("Host not found"));
+ break;
+ case ConnectionRefused:
+ q->setError(QAbstractSocket::ConnectionRefusedError,
+ QAbstractSocket::tr("Connection refused"));
+ break;
+ case TTLExpired:
+ q->setError(QAbstractSocket::NetworkError,
+ QSocks5SocketEngine::tr("TTL expired"));
+ break;
+ case CommandNotSupported:
+ q->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QSocks5SocketEngine::tr("SOCKSv5 command not supported"));
+ break;
+ case AddressTypeNotSupported:
+ q->setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QSocks5SocketEngine::tr("Address type not supported"));
+ break;
+
+ default:
+ q->setError(QAbstractSocket::UnknownSocketError,
+ QSocks5SocketEngine::tr("Unknown SOCKSv5 proxy error code 0x%1").arg(int(socks5error), 16));
+ break;
+ }
+
+ setErrorState(state, QString());
+}
+
+void QSocks5SocketEnginePrivate::reauthenticate()
+{
+ Q_Q(QSocks5SocketEngine);
+
+ // we require authentication
+ QAuthenticator auth;
+ emit q->proxyAuthenticationRequired(proxyInfo, &auth);
+
+ if (!auth.user().isEmpty() || !auth.password().isEmpty()) {
+ // we have new credentials, let's try again
+ QSOCKS5_DEBUG << "authentication failure: retrying connection";
+ socks5State = QSocks5SocketEnginePrivate::Uninitialized;
+
+ delete data->authenticator;
+ proxyInfo.setUser(auth.user());
+ proxyInfo.setPassword(auth.password());
+ data->authenticator = new QSocks5PasswordAuthenticator(proxyInfo.user(), proxyInfo.password());
+
+ data->controlSocket->blockSignals(true);
+ data->controlSocket->abort();
+ data->controlSocket->blockSignals(false);
+ data->controlSocket->connectToHost(proxyInfo.hostName(), proxyInfo.port());
+ } else {
+ // authentication failure
+
+ setErrorState(AuthenticatingError);
+ data->controlSocket->close();
+ emitConnectionNotification();
+ }
+}
+
+void QSocks5SocketEnginePrivate::parseAuthenticationMethodReply()
+{
+ // not enough data to begin
+ if (data->controlSocket->bytesAvailable() < 2)
+ return;
+
+ QByteArray buf = data->controlSocket->read(2);
+ if (buf.at(0) != S5_VERSION_5) {
+ QSOCKS5_D_DEBUG << "Socks5 version incorrect";
+ setErrorState(SocksError);
+ data->controlSocket->close();
+ emitConnectionNotification();
+ return;
+ }
+
+ bool authComplete = false;
+ if (uchar(buf.at(1)) == S5_AUTHMETHOD_NONE) {
+ authComplete = true;
+ } else if (uchar(buf.at(1)) == S5_AUTHMETHOD_NOTACCEPTABLE) {
+ reauthenticate();
+ return;
+ } else if (buf.at(1) != data->authenticator->methodId()
+ || !data->authenticator->beginAuthenticate(data->controlSocket, &authComplete)) {
+ setErrorState(AuthenticatingError, QLatin1String("Socks5 host did not support authentication method."));
+ socketError = QAbstractSocket::SocketAccessError; // change the socket error
+ emitConnectionNotification();
+ return;
+ }
+
+ if (authComplete)
+ sendRequestMethod();
+ else
+ socks5State = Authenticating;
+}
+
+void QSocks5SocketEnginePrivate::parseAuthenticatingReply()
+{
+ bool authComplete = false;
+ if (!data->authenticator->continueAuthenticate(data->controlSocket, &authComplete)) {
+ reauthenticate();
+ return;
+ }
+ if (authComplete)
+ sendRequestMethod();
+}
+
+void QSocks5SocketEnginePrivate::sendRequestMethod()
+{
+ QHostAddress address;
+ quint16 port = 0;
+ char command = 0;
+ if (mode == ConnectMode) {
+ command = S5_CONNECT;
+ address = peerAddress;
+ port = peerPort;
+ } else if (mode == BindMode) {
+ command = S5_BIND;
+ address = localAddress;
+ port = localPort;
+ } else {
+#ifndef QT_NO_UDPSOCKET
+ command = S5_UDP_ASSOCIATE;
+ address = localAddress; //data->controlSocket->localAddress();
+ port = localPort;
+#endif
+ }
+
+ QByteArray buf;
+ buf.reserve(270); // big enough for domain name;
+ buf[0] = S5_VERSION_5;
+ buf[1] = command;
+ buf[2] = 0x00;
+ if (peerName.isEmpty() && !qt_socks5_set_host_address_and_port(address, port, &buf)) {
+ QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
+ //### set error code ....
+ return;
+ } else if (!peerName.isEmpty() && !qt_socks5_set_host_name_and_port(peerName, port, &buf)) {
+ QSOCKS5_DEBUG << "error setting address" << address << " : " << port;
+ //### set error code ....
+ return;
+ }
+ QSOCKS5_DEBUG << "sending" << dump(buf);
+ QByteArray sealedBuf;
+ if (!data->authenticator->seal(buf, &sealedBuf)) {
+ // ### Handle this error.
+ }
+ data->controlSocket->write(sealedBuf);
+ data->controlSocket->flush();
+ socks5State = RequestMethodSent;
+}
+
+void QSocks5SocketEnginePrivate::parseRequestMethodReply()
+{
+ Q_Q(QSocks5SocketEngine);
+ QSOCKS5_DEBUG << "parseRequestMethodReply()";
+
+ QByteArray inBuf;
+ if (!data->authenticator->unSeal(data->controlSocket, &inBuf)) {
+ // ### check error and not just not enough data
+ QSOCKS5_DEBUG << "unSeal failed, needs more data";
+ return;
+ }
+ QSOCKS5_DEBUG << dump(inBuf);
+ if (inBuf.size() < 2) {
+ QSOCKS5_DEBUG << "need more data for request reply header .. put this data somewhere";
+ return;
+ }
+
+ QHostAddress address;
+ quint16 port = 0;
+
+ if (inBuf.at(0) != S5_VERSION_5 || inBuf.length() < 3 || inBuf.at(2) != 0x00) {
+ QSOCKS5_DEBUG << "socks protocol error";
+ setErrorState(SocksError);
+ } else if (inBuf.at(1) != S5_SUCCESS) {
+ Socks5Error socks5Error = Socks5Error(inBuf.at(1));
+ QSOCKS5_DEBUG << "Request error :" << socks5Error;
+ if ((socks5Error == SocksFailure || socks5Error == ConnectionNotAllowed)
+ && !peerName.isEmpty()) {
+ // Dante seems to use this error code to indicate hostname resolution failure
+ setErrorState(HostNameLookupError);
+ } else {
+ setErrorState(RequestError, socks5Error);
+ }
+ } else {
+ // connection success, retrieve the remote addresses
+ int pos = 3;
+ if (!qt_socks5_get_host_address_and_port(inBuf, &address, &port, &pos)) {
+ QSOCKS5_DEBUG << "error getting address";
+ setErrorState(SocksError);
+ } else {
+ inBuf.remove(0, pos);
+ for (int i = inBuf.size() - 1; i >= 0 ; --i)
+ data->controlSocket->ungetChar(inBuf.at(i));
+ }
+ }
+
+ if (socks5State == RequestMethodSent) {
+ // no error
+ localAddress = address;
+ localPort = port;
+
+ if (mode == ConnectMode) {
+ socks5State = Connected;
+ // notify the upper layer that we're done
+ q->setState(QAbstractSocket::ConnectedState);
+ emitConnectionNotification();
+ } else if (mode == BindMode) {
+ socks5State = BindSuccess;
+ q->setState(QAbstractSocket::ListeningState);
+ } else {
+ socks5State = UdpAssociateSuccess;
+ }
+ } else if (socks5State == BindSuccess) {
+ // no error and we got a connection
+ bindData->peerAddress = address;
+ bindData->peerPort = port;
+
+ emitReadNotification();
+ } else {
+ // got an error
+ data->controlSocket->close();
+ emitConnectionNotification();
+ }
+}
+
+void QSocks5SocketEnginePrivate::_q_emitPendingReadNotification()
+{
+ Q_Q(QSocks5SocketEngine);
+ readNotificationPending = false;
+ if (readNotificationEnabled) {
+ QSOCKS5_D_DEBUG << "emitting readNotification";
+ QPointer<QSocks5SocketEngine> qq = q;
+ emit q->readNotification();
+ if (!qq)
+ return;
+ // check if there needs to be a new zero read notification
+ if (data && data->controlSocket->state() == QAbstractSocket::UnconnectedState
+ && data->controlSocket->error() == QAbstractSocket::RemoteHostClosedError) {
+ connectData->readBuffer.clear();
+ emitReadNotification();
+ }
+ }
+}
+
+void QSocks5SocketEnginePrivate::emitReadNotification()
+{
+ Q_Q(QSocks5SocketEngine);
+ readNotificationActivated = true;
+ if (readNotificationEnabled && !readNotificationPending) {
+ QSOCKS5_D_DEBUG << "queueing readNotification";
+ readNotificationPending = true;
+ QMetaObject::invokeMethod(q, "_q_emitPendingReadNotification", Qt::QueuedConnection);
+ }
+}
+
+void QSocks5SocketEnginePrivate::_q_emitPendingWriteNotification()
+{
+ writeNotificationPending = false;
+ Q_Q(QSocks5SocketEngine);
+ if (writeNotificationEnabled) {
+ QSOCKS5_D_DEBUG << "emitting writeNotification";
+ emit q->writeNotification();
+ }
+}
+
+void QSocks5SocketEnginePrivate::emitWriteNotification()
+{
+ Q_Q(QSocks5SocketEngine);
+ writeNotificationActivated = true;
+ if (writeNotificationEnabled && !writeNotificationPending) {
+ QSOCKS5_D_DEBUG << "queueing writeNotification";
+ writeNotificationPending = true;
+ QMetaObject::invokeMethod(q, "_q_emitPendingWriteNotification", Qt::QueuedConnection);
+ }
+}
+
+void QSocks5SocketEnginePrivate::_q_emitPendingConnectionNotification()
+{
+ connectionNotificationPending = false;
+ Q_Q(QSocks5SocketEngine);
+ QSOCKS5_D_DEBUG << "emitting connectionNotification";
+ emit q->connectionNotification();
+}
+
+void QSocks5SocketEnginePrivate::emitConnectionNotification()
+{
+ Q_Q(QSocks5SocketEngine);
+ QSOCKS5_D_DEBUG << "queueing connectionNotification";
+ connectionNotificationPending = true;
+ QMetaObject::invokeMethod(q, "_q_emitPendingConnectionNotification", Qt::QueuedConnection);
+}
+
+QSocks5SocketEngine::QSocks5SocketEngine(QObject *parent)
+:QAbstractSocketEngine(*new QSocks5SocketEnginePrivate(), parent)
+{
+}
+
+QSocks5SocketEngine::~QSocks5SocketEngine()
+{
+ Q_D(QSocks5SocketEngine);
+
+ if (d->data) {
+ delete d->data->authenticator;
+ delete d->data->controlSocket;
+ }
+ if (d->connectData)
+ delete d->connectData;
+#ifndef QT_NO_UDPSOCKET
+ if (d->udpData) {
+ delete d->udpData->udpSocket;
+ delete d->udpData;
+ }
+#endif
+ if (d->bindData)
+ delete d->bindData;
+}
+
+static QBasicAtomicInt descriptorCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+bool QSocks5SocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
+{
+ Q_D(QSocks5SocketEngine);
+
+ d->socketDescriptor = descriptorCounter.fetchAndAddRelaxed(1);
+
+ d->socketType = type;
+ d->socketProtocol = protocol;
+
+ return true;
+}
+
+bool QSocks5SocketEngine::initialize(int socketDescriptor, QAbstractSocket::SocketState socketState)
+{
+ Q_D(QSocks5SocketEngine);
+
+ QSOCKS5_Q_DEBUG << "initialize" << socketDescriptor;
+
+ // this is only valid for the other side of a bind, nothing else is supported
+
+ if (socketState != QAbstractSocket::ConnectedState) {
+ //### must be connected state ???
+ return false;
+ }
+
+ QSocks5BindData *bindData = socks5BindStore()->retrieve(socketDescriptor);
+ if (bindData) {
+
+ d->socketState = QAbstractSocket::ConnectedState;
+ d->socketType = QAbstractSocket::TcpSocket;
+ d->connectData = new QSocks5ConnectData;
+ d->data = d->connectData;
+ d->mode = QSocks5SocketEnginePrivate::ConnectMode;
+ d->data->controlSocket = bindData->controlSocket;
+ bindData->controlSocket = 0;
+ d->data->controlSocket->setParent(this);
+ d->socketProtocol = d->data->controlSocket->localAddress().protocol();
+ d->data->authenticator = bindData->authenticator;
+ bindData->authenticator = 0;
+ d->localPort = bindData->localPort;
+ d->localAddress = bindData->localAddress;
+ d->peerPort = bindData->peerPort;
+ d->peerAddress = bindData->peerAddress;
+ delete bindData;
+
+ QObject::connect(d->data->controlSocket, SIGNAL(connected()), this, SLOT(_q_controlSocketConnected()),
+ Qt::DirectConnection);
+ QObject::connect(d->data->controlSocket, SIGNAL(readyRead()), this, SLOT(_q_controlSocketReadNotification()),
+ Qt::DirectConnection);
+ QObject::connect(d->data->controlSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(_q_controlSocketBytesWritten()),
+ Qt::DirectConnection);
+ QObject::connect(d->data->controlSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_controlSocketError(QAbstractSocket::SocketError)),
+ Qt::DirectConnection);
+ QObject::connect(d->data->controlSocket, SIGNAL(disconnected()), this, SLOT(_q_controlSocketDisconnected()),
+ Qt::DirectConnection);
+ QObject::connect(d->data->controlSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ this, SLOT(_q_controlSocketStateChanged(QAbstractSocket::SocketState)),
+ Qt::DirectConnection);
+
+ d->socks5State = QSocks5SocketEnginePrivate::Connected;
+
+ if (d->data->controlSocket->bytesAvailable() != 0)
+ d->_q_controlSocketReadNotification();
+ return true;
+ }
+ return false;
+}
+
+void QSocks5SocketEngine::setProxy(const QNetworkProxy &networkProxy)
+{
+ Q_D(QSocks5SocketEngine);
+ d->proxyInfo = networkProxy;
+}
+
+int QSocks5SocketEngine::socketDescriptor() const
+{
+ Q_D(const QSocks5SocketEngine);
+ return d->socketDescriptor;
+}
+
+bool QSocks5SocketEngine::isValid() const
+{
+ Q_D(const QSocks5SocketEngine);
+ return d->socketType != QAbstractSocket::UnknownSocketType
+ && d->socks5State != QSocks5SocketEnginePrivate::SocksError
+ && (d->socketError == QAbstractSocket::UnknownSocketError
+ || d->socketError == QAbstractSocket::SocketTimeoutError
+ || d->socketError == QAbstractSocket::UnfinishedSocketOperationError);
+}
+
+bool QSocks5SocketEngine::connectInternal()
+{
+ Q_D(QSocks5SocketEngine);
+
+ if (!d->data) {
+ if (socketType() == QAbstractSocket::TcpSocket) {
+ d->initialize(QSocks5SocketEnginePrivate::ConnectMode);
+#ifndef QT_NO_UDPSOCKET
+ } else if (socketType() == QAbstractSocket::UdpSocket) {
+ d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
+ // all udp needs to be bound
+ if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0))
+ return false;
+
+ setState(QAbstractSocket::ConnectedState);
+ return true;
+#endif
+ } else {
+ qFatal("QSocks5SocketEngine::connectToHost: in QTcpServer mode");
+ return false;
+ }
+ }
+
+ if (d->socks5State == QSocks5SocketEnginePrivate::Uninitialized
+ && d->socketState != QAbstractSocket::ConnectingState) {
+ setState(QAbstractSocket::ConnectingState);
+ d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
+ return false;
+ }
+ return false;
+}
+
+bool QSocks5SocketEngine::connectToHost(const QHostAddress &address, quint16 port)
+{
+ Q_D(QSocks5SocketEngine);
+ QSOCKS5_DEBUG << "connectToHost" << address << ':' << port;
+
+ setPeerAddress(address);
+ setPeerPort(port);
+ d->peerName.clear();
+
+ return connectInternal();
+}
+
+bool QSocks5SocketEngine::connectToHostByName(const QString &hostname, quint16 port)
+{
+ Q_D(QSocks5SocketEngine);
+
+ setPeerAddress(QHostAddress());
+ setPeerPort(port);
+ d->peerName = hostname;
+
+ return connectInternal();
+}
+
+void QSocks5SocketEnginePrivate::_q_controlSocketConnected()
+{
+ QSOCKS5_DEBUG << "_q_controlSocketConnected";
+ QByteArray buf(3, 0);
+ buf[0] = S5_VERSION_5;
+ buf[1] = 0x01;
+ buf[2] = data->authenticator->methodId();
+ data->controlSocket->write(buf);
+ socks5State = AuthenticationMethodsSent;
+}
+
+void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
+{
+ QSOCKS5_D_DEBUG << "_q_controlSocketReadNotification socks5state" << s5StateToString(socks5State)
+ << "bytes available" << data->controlSocket->bytesAvailable();
+
+ if (data->controlSocket->bytesAvailable() == 0) {
+ QSOCKS5_D_DEBUG << "########## bogus read why do we get these ... on windows only";
+ return;
+ }
+
+ switch (socks5State) {
+ case AuthenticationMethodsSent:
+ parseAuthenticationMethodReply();
+ break;
+ case Authenticating:
+ parseAuthenticatingReply();
+ break;
+ case RequestMethodSent:
+ parseRequestMethodReply();
+ break;
+ case Connected: {
+ QByteArray buf;
+ if (!data->authenticator->unSeal(data->controlSocket, &buf)) {
+ // qDebug() << "unseal error maybe need to wait for more data";
+ }
+ if (buf.size()) {
+ QSOCKS5_DEBUG << dump(buf);
+ connectData->readBuffer += buf;
+ emitReadNotification();
+ }
+ break;
+ }
+ case BindSuccess:
+ // only get here if command is bind
+ if (mode == BindMode) {
+ parseRequestMethodReply();
+ break;
+ }
+
+ // fall through
+ default:
+ qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
+ "Unexpectedly received data while in state=%d and mode=%d",
+ socks5State, mode);
+ break;
+ };
+}
+
+void QSocks5SocketEnginePrivate::_q_controlSocketBytesWritten()
+{
+ QSOCKS5_DEBUG << "_q_controlSocketBytesWritten";
+
+ if (socks5State != Connected
+ || (mode == ConnectMode
+ && data->controlSocket->bytesToWrite()))
+ return;
+ if (data->controlSocket->bytesToWrite() < MaxWriteBufferSize) {
+ emitWriteNotification();
+ writeNotificationActivated = false;
+ }
+}
+
+void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketError error)
+{
+ QSOCKS5_D_DEBUG << "controlSocketError" << error << data->controlSocket->errorString();
+
+ if (error == QAbstractSocket::SocketTimeoutError)
+ return; // ignore this error -- comes from the waitFor* functions
+
+ if (error == QAbstractSocket::RemoteHostClosedError
+ && socks5State == Connected) {
+ // clear the read buffer in connect mode so that bytes available returns 0
+ // if there already is a read notification pending then this will be processed first
+ if (!readNotificationPending)
+ connectData->readBuffer.clear();
+ emitReadNotification();
+ data->controlSocket->close();
+ // cause a disconnect in the outer socket
+ emitWriteNotification();
+ } else if (socks5State == Uninitialized
+ || socks5State == AuthenticationMethodsSent
+ || socks5State == Authenticating
+ || socks5State == RequestMethodSent) {
+ setErrorState(socks5State == Uninitialized ? ConnectError : ControlSocketError);
+ data->controlSocket->close();
+ emitConnectionNotification();
+ } else {
+ q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString());
+ emitReadNotification();
+ emitWriteNotification();
+ }
+}
+
+void QSocks5SocketEnginePrivate::_q_controlSocketDisconnected()
+{
+ QSOCKS5_D_DEBUG << "_q_controlSocketDisconnected";
+}
+
+void QSocks5SocketEnginePrivate::_q_controlSocketStateChanged(QAbstractSocket::SocketState state)
+{
+ QSOCKS5_D_DEBUG << "_q_controlSocketStateChanged" << state;
+}
+
+#ifndef QT_NO_UDPSOCKET
+void QSocks5SocketEnginePrivate::checkForDatagrams() const
+{
+ // udp should be unbuffered so we need to do some polling at certain points
+ if (udpData->udpSocket->hasPendingDatagrams())
+ const_cast<QSocks5SocketEnginePrivate *>(this)->_q_udpSocketReadNotification();
+}
+
+void QSocks5SocketEnginePrivate::_q_udpSocketReadNotification()
+{
+ QSOCKS5_D_DEBUG << "_q_udpSocketReadNotification()";
+
+ // check some state stuff
+ if (!udpData->udpSocket->hasPendingDatagrams()) {
+ QSOCKS5_D_DEBUG << "false read ??";
+ return;
+ }
+
+ while (udpData->udpSocket->hasPendingDatagrams()) {
+ QByteArray sealedBuf(udpData->udpSocket->pendingDatagramSize(), 0);
+ QSOCKS5_D_DEBUG << "new datagram";
+ udpData->udpSocket->readDatagram(sealedBuf.data(), sealedBuf.size());
+ QByteArray inBuf;
+ if (!data->authenticator->unSeal(sealedBuf, &inBuf)) {
+ QSOCKS5_D_DEBUG << "failed unsealing datagram discarding";
+ return;
+ }
+ QSOCKS5_DEBUG << dump(inBuf);
+ int pos = 0;
+ const char *buf = inBuf.constData();
+ if (inBuf.size() < 4) {
+ QSOCKS5_D_DEBUG << "bugus udp data, discarding";
+ return;
+ }
+ QSocks5RevivedDatagram datagram;
+ if (buf[pos++] != 0 || buf[pos++] != 0) {
+ QSOCKS5_D_DEBUG << "invalid datagram discarding";
+ return;
+ }
+ if (buf[pos++] != 0) { //### add fragmentation reading support
+ QSOCKS5_D_DEBUG << "don't support fragmentation yet disgarding";
+ return;
+ }
+ if (!qt_socks5_get_host_address_and_port(inBuf, &datagram.address, &datagram.port, &pos)) {
+ QSOCKS5_D_DEBUG << "failed to get address from datagram disgarding";
+ return;
+ }
+ datagram.data = QByteArray(&buf[pos], inBuf.size() - pos);
+ udpData->pendingDatagrams.enqueue(datagram);
+ }
+ emitReadNotification();
+}
+#endif // QT_NO_UDPSOCKET
+
+bool QSocks5SocketEngine::bind(const QHostAddress &address, quint16 port)
+{
+ Q_D(QSocks5SocketEngine);
+
+ // when bind wee will block until the bind is finished as the info from the proxy server is needed
+
+ if (!d->data) {
+ if (socketType() == QAbstractSocket::TcpSocket) {
+ d->initialize(QSocks5SocketEnginePrivate::BindMode);
+#ifndef QT_NO_UDPSOCKET
+ } else if (socketType() == QAbstractSocket::UdpSocket) {
+ d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
+#endif
+ } else {
+ //### something invalid
+ return false;
+ }
+ }
+
+#ifndef QT_NO_UDPSOCKET
+ if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
+ if (!d->udpData->udpSocket->bind(address, port)) {
+ QSOCKS5_Q_DEBUG << "local udp bind failed";
+ setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
+ return false;
+ }
+ d->localAddress = d->udpData->udpSocket->localAddress();
+ d->localPort = d->udpData->udpSocket->localPort();
+ } else
+#endif
+ if (d->mode == QSocks5SocketEnginePrivate::BindMode) {
+ d->localAddress = address;
+ d->localPort = port;
+ } else {
+ //### something invalid
+ return false;
+ }
+
+ int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+ d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
+ if (!d->waitForConnected(msecs, 0) ||
+ d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
+ // waitForConnected sets the error state and closes the socket
+ QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
+ return false;
+ }
+ if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
+ setState(QAbstractSocket::BoundState);
+ return true;
+#ifndef QT_NO_UDPSOCKET
+ } else if (d->socks5State == QSocks5SocketEnginePrivate::UdpAssociateSuccess) {
+ setState(QAbstractSocket::BoundState);
+ d->udpData->associateAddress = d->localAddress;
+ d->localAddress = QHostAddress();
+ 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
+ || !dummy.waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))
+ || dummy.readDatagram(0,0, &d->localAddress, &d->localPort) != 0) {
+ QSOCKS5_DEBUG << "udp actual address and port lookup failed";
+ setState(QAbstractSocket::UnconnectedState);
+ setError(dummy.error(), dummy.errorString());
+ d->data->controlSocket->close();
+ //### reset and error
+ return false;
+ }
+ QSOCKS5_DEBUG << "udp actual address and port" << d->localAddress << ':' << d->localPort;
+ return true;
+#endif // QT_NO_UDPSOCKET
+ }
+
+ // binding timed out
+ setError(QAbstractSocket::SocketTimeoutError,
+ QLatin1String(QT_TRANSLATE_NOOP("QSocks5SocketEngine", "Network operation timed out")));
+
+///### delete d->udpSocket;
+///### d->udpSocket = 0;
+ return false;
+}
+
+
+bool QSocks5SocketEngine::listen()
+{
+ Q_D(QSocks5SocketEngine);
+
+ QSOCKS5_Q_DEBUG << "listen()";
+
+ // check that we are in bound and then go to listening.
+ if (d->socketState == QAbstractSocket::BoundState) {
+ d->socketState = QAbstractSocket::ListeningState;
+
+ // check if we already have a connection
+ if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
+ d->emitReadNotification();
+
+ return true;
+ }
+ return false;
+}
+
+int QSocks5SocketEngine::accept()
+{
+ Q_D(QSocks5SocketEngine);
+ // check we are listing ---
+
+ QSOCKS5_Q_DEBUG << "accept()";
+
+ if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) {
+ QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store";
+ d->data->controlSocket->disconnect();
+ d->data->controlSocket->setParent(0);
+ d->bindData->localAddress = d->localAddress;
+ d->bindData->localPort = d->localPort;
+ int sd = d->socketDescriptor;
+ socks5BindStore()->add(sd, d->bindData);
+ d->data = 0;
+ d->bindData = 0;
+ d->socketDescriptor = 0;
+ //### do something about this socket layer ... set it closed and an error about why ...
+ // reset state and local port/address
+ d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..??
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return sd;
+ }
+ return -1;
+}
+
+void QSocks5SocketEngine::close()
+{
+ QSOCKS5_Q_DEBUG << "close()";
+ Q_D(QSocks5SocketEngine);
+ if (d->data && d->data->controlSocket) {
+ if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
+ int msecs = 100;
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+ while (!d->data->controlSocket->bytesToWrite()) {
+ if (!d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed())))
+ break;
+ }
+ }
+ d->data->controlSocket->close();
+ }
+#ifndef QT_NO_UDPSOCKET
+ if (d->udpData && d->udpData->udpSocket)
+ d->udpData->udpSocket->close();
+#endif
+}
+
+qint64 QSocks5SocketEngine::bytesAvailable() const
+{
+ Q_D(const QSocks5SocketEngine);
+ if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
+ return d->connectData->readBuffer.size();
+#ifndef QT_NO_UDPSOCKET
+ else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
+ && !d->udpData->pendingDatagrams.isEmpty())
+ return d->udpData->pendingDatagrams.first().data.size();
+#endif
+ return 0;
+}
+
+qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
+{
+ Q_D(QSocks5SocketEngine);
+ QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
+ if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
+ if (d->connectData->readBuffer.size() == 0) {
+ if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
+ //imitate remote closed
+ close();
+ setError(QAbstractSocket::RemoteHostClosedError,
+ QLatin1String("Remote host closed connection###"));
+ setState(QAbstractSocket::UnconnectedState);
+ return -1;
+ } else {
+ return 0; // nothing to be read
+ }
+ }
+ qint64 copy = qMin<qint64>(d->connectData->readBuffer.size(), maxlen);
+ memcpy(data, d->connectData->readBuffer.constData(), copy);
+ d->connectData->readBuffer.remove(0, copy);
+ QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
+ return copy;
+#ifndef QT_NO_UDPSOCKET
+ } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
+ return readDatagram(data, maxlen);
+#endif
+ }
+ return 0;
+}
+
+qint64 QSocks5SocketEngine::write(const char *data, qint64 len)
+{
+ Q_D(QSocks5SocketEngine);
+ QSOCKS5_Q_DEBUG << "write" << dump(QByteArray(data, len));
+
+ if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
+ // clamp down the amount of bytes to transfer at once
+ len = qMin<qint64>(len, MaxWriteBufferSize) - d->data->controlSocket->bytesToWrite();
+ if (len <= 0)
+ return 0;
+
+ QByteArray buf = QByteArray::fromRawData(data, len);
+ QByteArray sealedBuf;
+ if (!d->data->authenticator->seal(buf, &sealedBuf)) {
+ // ### Handle this error.
+ }
+
+ d->data->controlSocket->write(sealedBuf);
+ d->data->controlSocket->waitForBytesWritten(0);
+ return len;
+#ifndef QT_NO_UDPSOCKET
+ } else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode) {
+ // send to connected address
+ return writeDatagram(data, len, d->peerAddress, d->peerPort);
+#endif
+ }
+ //### set an error ???
+ return -1;
+}
+
+#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)
+{
+ Q_D(QSocks5SocketEngine);
+
+ d->checkForDatagrams();
+
+ if (d->udpData->pendingDatagrams.isEmpty())
+ return 0;
+
+ QSocks5RevivedDatagram datagram = d->udpData->pendingDatagrams.dequeue();
+ int copyLen = qMin<int>(maxlen, datagram.data.size());
+ memcpy(data, datagram.data.constData(), copyLen);
+ if (addr)
+ *addr = datagram.address;
+ if (port)
+ *port = datagram.port;
+ return copyLen;
+}
+
+qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QHostAddress &address,
+ quint16 port)
+{
+ Q_D(QSocks5SocketEngine);
+
+ // it is possible to send with out first binding with udp, but socks5 requires a bind.
+ if (!d->data) {
+ d->initialize(QSocks5SocketEnginePrivate::UdpAssociateMode);
+ // all udp needs to be bound
+ if (!bind(QHostAddress(QLatin1String("0.0.0.0")), 0)) {
+ //### set error
+ return -1;
+ }
+ }
+
+ QByteArray outBuf;
+ outBuf.reserve(270 + len);
+ outBuf[0] = 0x00;
+ outBuf[1] = 0x00;
+ outBuf[2] = 0x00;
+ if (!qt_socks5_set_host_address_and_port(address, port, &outBuf)) {
+ }
+ outBuf += QByteArray(data, len);
+ QSOCKS5_DEBUG << "sending" << dump(outBuf);
+ QByteArray sealedBuf;
+ if (!d->data->authenticator->seal(outBuf, &sealedBuf)) {
+ QSOCKS5_DEBUG << "sealing data failed";
+ setError(QAbstractSocket::SocketAccessError, d->data->authenticator->errorString());
+ return -1;
+ }
+ if (d->udpData->udpSocket->writeDatagram(sealedBuf, d->udpData->associateAddress, d->udpData->associatePort) != sealedBuf.size()) {
+ //### try frgamenting
+ if (d->udpData->udpSocket->error() == QAbstractSocket::DatagramTooLargeError)
+ setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
+ //### else maybe more serious error
+ return -1;
+ }
+
+ return len;
+}
+
+bool QSocks5SocketEngine::hasPendingDatagrams() const
+{
+ Q_D(const QSocks5SocketEngine);
+ Q_INIT_CHECK(false);
+
+ d->checkForDatagrams();
+
+ return !d->udpData->pendingDatagrams.isEmpty();
+}
+
+qint64 QSocks5SocketEngine::pendingDatagramSize() const
+{
+ Q_D(const QSocks5SocketEngine);
+
+ d->checkForDatagrams();
+
+ if (!d->udpData->pendingDatagrams.isEmpty())
+ return d->udpData->pendingDatagrams.head().data.size();
+ return 0;
+}
+#endif // QT_NO_UDPSOCKET
+
+qint64 QSocks5SocketEngine::bytesToWrite() const
+{
+ Q_D(const QSocks5SocketEngine);
+ if (d->data && d->data->controlSocket) {
+ return d->data->controlSocket->bytesToWrite();
+ } else {
+ return 0;
+ }
+}
+
+int QSocks5SocketEngine::option(SocketOption option) const
+{
+ Q_D(const QSocks5SocketEngine);
+ if (d->data && d->data->controlSocket) {
+ // convert the enum and call the real socket
+ if (option == QAbstractSocketEngine::LowDelayOption)
+ return d->data->controlSocket->socketOption(QAbstractSocket::LowDelayOption).toInt();
+ if (option == QAbstractSocketEngine::KeepAliveOption)
+ return d->data->controlSocket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
+ }
+ return -1;
+}
+
+bool QSocks5SocketEngine::setOption(SocketOption option, int value)
+{
+ Q_D(QSocks5SocketEngine);
+ if (d->data && d->data->controlSocket) {
+ // convert the enum and call the real socket
+ if (option == QAbstractSocketEngine::LowDelayOption)
+ d->data->controlSocket->setSocketOption(QAbstractSocket::LowDelayOption, value);
+ if (option == QAbstractSocketEngine::KeepAliveOption)
+ d->data->controlSocket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
+ return true;
+ }
+ return false;
+}
+
+bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
+{
+ if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
+ return false;
+
+ const Socks5State wantedState =
+ mode == ConnectMode ? Connected :
+ mode == BindMode ? BindSuccess :
+ UdpAssociateSuccess;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ while (socks5State != wantedState) {
+ if (!data->controlSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
+ return true;
+
+ setErrorState(QSocks5SocketEnginePrivate::ControlSocketError);
+ if (timedOut && data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
+ *timedOut = true;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
+{
+ Q_D(QSocks5SocketEngine);
+ QSOCKS5_DEBUG << "waitForRead" << msecs;
+
+ d->readNotificationActivated = false;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // are we connected yet?
+ if (!d->waitForConnected(msecs, timedOut))
+ return false;
+ if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
+ return true;
+
+ // we're connected
+ if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
+ d->mode == QSocks5SocketEnginePrivate::BindMode) {
+ while (!d->readNotificationActivated) {
+ if (!d->data->controlSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
+ return true;
+
+ setError(d->data->controlSocket->error(), d->data->controlSocket->errorString());
+ if (timedOut && d->data->controlSocket->error() == QAbstractSocket::SocketTimeoutError)
+ *timedOut = true;
+ return false;
+ }
+ }
+#ifndef QT_NO_UDPSOCKET
+ } else {
+ while (!d->readNotificationActivated) {
+ if (!d->udpData->udpSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
+ if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
+ *timedOut = true;
+ return false;
+ }
+ }
+#endif // QT_NO_UDPSOCKET
+ }
+
+
+ bool ret = d->readNotificationActivated;
+ d->readNotificationActivated = false;
+
+ QSOCKS5_DEBUG << "waitForRead returned" << ret;
+ return ret;
+}
+
+
+bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
+{
+ Q_D(QSocks5SocketEngine);
+ QSOCKS5_DEBUG << "waitForWrite" << msecs;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ // are we connected yet?
+ if (!d->waitForConnected(msecs, timedOut))
+ return false;
+ if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
+ return true;
+
+ // we're connected
+
+ // flush any bytes we may still have buffered in the time that we have left
+ if (d->data->controlSocket->bytesToWrite())
+ d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
+ while ((msecs == -1 || stopWatch.elapsed() < msecs)
+ && d->data->controlSocket->state() == QAbstractSocket::ConnectedState
+ && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
+ d->data->controlSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
+ return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
+}
+
+bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
+ bool checkRead, bool checkWrite,
+ int msecs, bool *timedOut)
+{
+ Q_UNUSED(checkRead);
+ if (!checkWrite) {
+ bool canRead = waitForRead(msecs, timedOut);
+ if (readyToRead)
+ *readyToRead = canRead;
+ return canRead;
+ }
+
+ bool canWrite = waitForWrite(msecs, timedOut);
+ if (readyToWrite)
+ *readyToWrite = canWrite;
+ return canWrite;
+}
+
+bool QSocks5SocketEngine::isReadNotificationEnabled() const
+{
+ Q_D(const QSocks5SocketEngine);
+ return d->readNotificationEnabled;
+}
+
+void QSocks5SocketEngine::setReadNotificationEnabled(bool enable)
+{
+ Q_D(QSocks5SocketEngine);
+
+ QSOCKS5_Q_DEBUG << "setReadNotificationEnabled(" << enable << ')';
+
+ bool emitSignal = false;
+ if (!d->readNotificationEnabled
+ && enable) {
+ if (d->mode == QSocks5SocketEnginePrivate::ConnectMode)
+ emitSignal = !d->connectData->readBuffer.isEmpty();
+#ifndef QT_NO_UDPSOCKET
+ else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode)
+ emitSignal = !d->udpData->pendingDatagrams.isEmpty();
+#endif
+ else if (d->mode == QSocks5SocketEnginePrivate::BindMode
+ && d->socketState == QAbstractSocket::ListeningState
+ && d->socks5State == QSocks5SocketEnginePrivate::BindSuccess)
+ emitSignal = true;
+ }
+
+ d->readNotificationEnabled = enable;
+
+ if (emitSignal)
+ d->emitReadNotification();
+}
+
+bool QSocks5SocketEngine::isWriteNotificationEnabled() const
+{
+ Q_D(const QSocks5SocketEngine);
+ return d->writeNotificationEnabled;
+}
+
+void QSocks5SocketEngine::setWriteNotificationEnabled(bool enable)
+{
+ Q_D(QSocks5SocketEngine);
+ d->writeNotificationEnabled = enable;
+ if (enable && d->socketState == QAbstractSocket::ConnectedState) {
+ if (d->mode == QSocks5SocketEnginePrivate::ConnectMode && d->data->controlSocket->bytesToWrite())
+ return; // will be emitted as a result of bytes written
+ d->emitWriteNotification();
+ d->writeNotificationActivated = false;
+ }
+}
+
+bool QSocks5SocketEngine::isExceptionNotificationEnabled() const
+{
+ Q_D(const QSocks5SocketEngine);
+ return d->exceptNotificationEnabled;
+}
+
+void QSocks5SocketEngine::setExceptionNotificationEnabled(bool enable)
+{
+ Q_D(QSocks5SocketEngine);
+ d->exceptNotificationEnabled = enable;
+}
+
+QAbstractSocketEngine *
+QSocks5SocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
+ const QNetworkProxy &proxy, QObject *parent)
+{
+ Q_UNUSED(socketType);
+
+ // proxy type must have been resolved by now
+ if (proxy.type() != QNetworkProxy::Socks5Proxy) {
+ QSOCKS5_DEBUG << "not proxying";
+ return 0;
+ }
+ QScopedPointer<QSocks5SocketEngine> engine(new QSocks5SocketEngine(parent));
+ engine->setProxy(proxy);
+ return engine.take();
+}
+
+QAbstractSocketEngine *QSocks5SocketEngineHandler::createSocketEngine(int socketDescriptor, QObject *parent)
+{
+ QSOCKS5_DEBUG << "createSocketEngine" << socketDescriptor;
+ if (socks5BindStore()->contains(socketDescriptor)) {
+ QSOCKS5_DEBUG << "bind store contains" << socketDescriptor;
+ return new QSocks5SocketEngine(parent);
+ }
+ return 0;
+}
+
+#endif // QT_NO_SOCKS5
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
new file mode 100644
index 0000000000..9492d4532d
--- /dev/null
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -0,0 +1,299 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSOCKS5SOCKETENGINE_P_H
+#define QSOCKS5SOCKETENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qabstractsocketengine_p.h"
+#include "qnetworkproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SOCKS5
+
+class QSocks5SocketEnginePrivate;
+
+class Q_AUTOTEST_EXPORT QSocks5SocketEngine : public QAbstractSocketEngine
+{
+ Q_OBJECT
+public:
+ QSocks5SocketEngine(QObject *parent = 0);
+ ~QSocks5SocketEngine();
+
+ bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol);
+ bool initialize(int socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState);
+
+ void setProxy(const QNetworkProxy &networkProxy);
+
+ int socketDescriptor() const;
+
+ bool isValid() const;
+
+ bool connectInternal();
+ 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();
+
+ qint64 bytesAvailable() const;
+
+ qint64 read(char *data, qint64 maxlen);
+ 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,
+ quint16 port);
+ bool hasPendingDatagrams() const;
+ qint64 pendingDatagramSize() const;
+#endif // QT_NO_UDPSOCKET
+
+ qint64 bytesToWrite() const;
+
+ 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);
+
+private:
+ Q_DECLARE_PRIVATE(QSocks5SocketEngine)
+ Q_DISABLE_COPY(QSocks5SocketEngine)
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketConnected())
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketReadNotification())
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketError(QAbstractSocket::SocketError))
+#ifndef QT_NO_UDPSOCKET
+ Q_PRIVATE_SLOT(d_func(), void _q_udpSocketReadNotification())
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketBytesWritten())
+ Q_PRIVATE_SLOT(d_func(), void _q_emitPendingReadNotification())
+ Q_PRIVATE_SLOT(d_func(), void _q_emitPendingWriteNotification())
+ Q_PRIVATE_SLOT(d_func(), void _q_emitPendingConnectionNotification())
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketDisconnected())
+ Q_PRIVATE_SLOT(d_func(), void _q_controlSocketStateChanged(QAbstractSocket::SocketState))
+
+};
+
+
+class QTcpSocket;
+
+class QSocks5Authenticator
+{
+public:
+ QSocks5Authenticator();
+ virtual ~QSocks5Authenticator();
+ virtual char methodId();
+ virtual bool beginAuthenticate(QTcpSocket *socket, bool *completed);
+ virtual bool continueAuthenticate(QTcpSocket *socket, bool *completed);
+
+ virtual bool seal(const QByteArray buf, QByteArray *sealedBuf);
+ virtual bool unSeal(const QByteArray sealedBuf, QByteArray *buf);
+ virtual bool unSeal(QTcpSocket *sealedSocket, QByteArray *buf);
+
+ virtual QString errorString() { return QString(); }
+};
+
+class QSocks5PasswordAuthenticator : public QSocks5Authenticator
+{
+public:
+ QSocks5PasswordAuthenticator(const QString &userName, const QString &password);
+ char methodId();
+ bool beginAuthenticate(QTcpSocket *socket, bool *completed);
+ bool continueAuthenticate(QTcpSocket *socket, bool *completed);
+
+ QString errorString();
+
+private:
+ QString userName;
+ QString password;
+};
+
+struct QSocks5Data;
+struct QSocks5ConnectData;
+struct QSocks5UdpAssociateData;
+struct QSocks5BindData;
+
+class QSocks5SocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QSocks5SocketEngine)
+public:
+ QSocks5SocketEnginePrivate();
+ ~QSocks5SocketEnginePrivate();
+
+ enum Socks5State
+ {
+ Uninitialized = 0,
+ ConnectError,
+ AuthenticationMethodsSent,
+ Authenticating,
+ AuthenticatingError,
+ RequestMethodSent,
+ RequestError,
+ Connected,
+ UdpAssociateSuccess,
+ BindSuccess,
+ ControlSocketError,
+ SocksError,
+ HostNameLookupError
+ };
+ Socks5State socks5State;
+
+ enum Socks5Mode
+ {
+ NoMode,
+ ConnectMode,
+ BindMode,
+ UdpAssociateMode
+ };
+ Socks5Mode mode;
+
+ enum Socks5Error
+ {
+ SocksFailure = 0x01,
+ ConnectionNotAllowed = 0x02,
+ NetworkUnreachable = 0x03,
+ HostUnreachable = 0x04,
+ ConnectionRefused = 0x05,
+ TTLExpired = 0x06,
+ CommandNotSupported = 0x07,
+ AddressTypeNotSupported = 0x08,
+ LastKnownError = AddressTypeNotSupported,
+ UnknownError
+ };
+
+ void initialize(Socks5Mode socks5Mode);
+
+ void setErrorState(Socks5State state, const QString &extraMessage = QString());
+ void setErrorState(Socks5State state, Socks5Error socks5error);
+
+ void reauthenticate();
+ void parseAuthenticationMethodReply();
+ void parseAuthenticatingReply();
+ void sendRequestMethod();
+ void parseRequestMethodReply();
+ void parseNewConnection();
+
+ bool waitForConnected(int msecs, bool *timedOut);
+
+ void _q_controlSocketConnected();
+ void _q_controlSocketReadNotification();
+ void _q_controlSocketError(QAbstractSocket::SocketError);
+#ifndef QT_NO_UDPSOCKET
+ void checkForDatagrams() const;
+ void _q_udpSocketReadNotification();
+#endif
+ void _q_controlSocketBytesWritten();
+ void _q_controlSocketDisconnected();
+ void _q_controlSocketStateChanged(QAbstractSocket::SocketState);
+
+ QNetworkProxy proxyInfo;
+
+ bool readNotificationEnabled, writeNotificationEnabled, exceptNotificationEnabled;
+
+ int socketDescriptor;
+
+ QSocks5Data *data;
+ QSocks5ConnectData *connectData;
+#ifndef QT_NO_UDPSOCKET
+ QSocks5UdpAssociateData *udpData;
+#endif
+ QSocks5BindData *bindData;
+ QString peerName;
+
+ mutable bool readNotificationActivated;
+ mutable bool writeNotificationActivated;
+
+ bool readNotificationPending;
+ void _q_emitPendingReadNotification();
+ void emitReadNotification();
+ bool writeNotificationPending;
+ void _q_emitPendingWriteNotification();
+ void emitWriteNotification();
+ bool connectionNotificationPending;
+ void _q_emitPendingConnectionNotification();
+ void emitConnectionNotification();
+};
+
+class Q_AUTOTEST_EXPORT QSocks5SocketEngineHandler : public QSocketEngineHandler
+{
+public:
+ virtual QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType,
+ const QNetworkProxy &, QObject *parent);
+ virtual QAbstractSocketEngine *createSocketEngine(int socketDescripter, QObject *parent);
+};
+
+
+QT_END_NAMESPACE
+#endif // QT_NO_SOCKS5
+#endif // QSOCKS5SOCKETENGINE_H
diff --git a/src/network/socket/qsymbiansocketengine.cpp b/src/network/socket/qsymbiansocketengine.cpp
new file mode 100644
index 0000000000..f1b2982626
--- /dev/null
+++ b/src/network/socket/qsymbiansocketengine.cpp
@@ -0,0 +1,1730 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#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
+ err = nativeSocket.Open(socketServer, family, type, protocol); //TODO: FIXME - deprecated API, make sure we always have a connection instead
+
+ 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)
+{
+}
+
+QSymbianSocketEnginePrivate::~QSymbianSocketEnginePrivate()
+{
+}
+
+
+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::connectionNotification()
+{
+ // FIXME check if we really need to do it like that in Symbian
+ Q_D(QSymbianSocketEngine);
+ Q_ASSERT(state() == QAbstractSocket::ConnectingState);
+
+ 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);
+ int nbytes;
+ 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...)
+ QByteArray next(nbytes,0);
+ TPtr8 buffer((TUint8*)next.data(), next.size());
+ TInetAddr addr;
+ TRequestStatus status;
+ //TODO: rather than peek, should we save this for next call to readDatagram?
+ //what if calls don't match though?
+ d->nativeSocket.RecvFrom(buffer, addr, KSockReadPeek, status);
+ User::WaitForRequest(status);
+ if (status.Int())
+ return 0;
+ return buffer.Length();
+ }
+ 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);
+ 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();
+}
+
+// FIXME check where the native socket engine called that..
+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;
+ }
+
+ //TODO: call nativeSocket.Shutdown(EImmediate) in some cases?
+ if (d->socketType == QAbstractSocket::UdpSocket) {
+ //TODO: Close hangs without this, but only for UDP - why?
+ 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();
+}
+
+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); //TODO: on emulator this blocks for write >16kB (non blocking IO not implemented properly?)
+ 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);
+
+ 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) {
+ //TODO: avoidable cast?
+ //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(); //TODO: in error case should we restart or not?
+ 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
+{
+ //TODO
+ 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)
+{
+ //TODO - 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;
+}
+
+// FIXME 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)) {
+ 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);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::isReadNotificationEnabled(), false);
+ return d->readNotificationsEnabled;
+}
+
+void QSymbianSocketEngine::setReadNotificationEnabled(bool enable)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setReadNotificationEnabled(), Q_VOID);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::setReadNotificationEnabled" << enable << "socket" << d->socketDescriptor;
+#endif
+ d->readNotificationsEnabled = enable;
+ startNotifications();
+}
+
+bool QSymbianSocketEngine::isWriteNotificationEnabled() const
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::isWriteNotificationEnabled(), false);
+ return d->writeNotificationsEnabled;
+}
+
+void QSymbianSocketEngine::setWriteNotificationEnabled(bool enable)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setWriteNotificationEnabled(), Q_VOID);
+#ifdef QNATIVESOCKETENGINE_DEBUG
+ qDebug() << "QSymbianSocketEngine::setWriteNotificationEnabled" << enable << "socket" << d->socketDescriptor;
+#endif
+ d->writeNotificationsEnabled = enable;
+ startNotifications();
+}
+
+bool QSymbianSocketEngine::isExceptionNotificationEnabled() const
+{
+ Q_D(const QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::isExceptionNotificationEnabled(), false);
+ return d->exceptNotificationsEnabled;
+ return false;
+}
+
+void QSymbianSocketEngine::setExceptionNotificationEnabled(bool enable)
+{
+ Q_D(QSymbianSocketEngine);
+ Q_CHECK_VALID_SOCKETLAYER(QSymbianSocketEngine::setExceptionNotificationEnabled(), Q_VOID);
+#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;
+ 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->connectionNotification();
+ 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 0000000000..85ab54af12
--- /dev/null
+++ b/src/network/socket/qsymbiansocketengine_p.h
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef 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();
+
+public Q_SLOTS:
+ // TODO: Why do we do this? This is private Qt implementation stuff anyway, no need for it
+ // non-virtual override;
+ void connectionNotification();
+
+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;
+
+ // FIXME this is duplicated from qnativesocketengine_p.h
+ 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
new file mode 100644
index 0000000000..5a60764a3b
--- /dev/null
+++ b/src/network/socket/qtcpserver.cpp
@@ -0,0 +1,691 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QTCPSERVER_DEBUG
+
+/*! \class QTcpServer
+
+ \brief The QTcpServer class provides a TCP-based server.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ This class makes it possible to accept incoming TCP connections.
+ You can specify the port or have QTcpServer pick one
+ automatically. You can listen on a specific address or on all the
+ machine's addresses.
+
+ Call listen() to have the server listen for incoming connections.
+ The newConnection() signal is then emitted each time a client
+ connects to the server.
+
+ Call nextPendingConnection() to accept the pending connection as
+ a connected QTcpSocket. The function returns a pointer to a
+ QTcpSocket in QAbstractSocket::ConnectedState that you can use for
+ communicating with the client.
+
+ If an error occurs, serverError() returns the type of error, and
+ errorString() can be called to get a human readable description of
+ what happened.
+
+ When listening for connections, the address and port on which the
+ server is listening are available as serverAddress() and
+ serverPort().
+
+ Calling close() makes QTcpServer stop listening for incoming
+ connections.
+
+ Although QTcpServer is mostly designed for use with an event
+ loop, it's possible to use it without one. In that case, you must
+ use waitForNewConnection(), which blocks until either a
+ connection is available or a timeout expires.
+
+ \section1 Symbian Platform Security Requirements
+
+ On Symbian, processes which use this class must have the
+ \c NetworkServices platform security capability. If the client
+ process lacks this capability, it will lead to a panic.
+
+ Platform security capabilities are added via the
+ \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
+ qmake variable.
+
+ \sa QTcpSocket, {Fortune Server Example}, {Threaded Fortune Server Example},
+ {Loopback Example}, {Torrent Example}
+*/
+
+/*! \fn void QTcpServer::newConnection()
+
+ This signal is emitted every time a new connection is available.
+
+ \sa hasPendingConnections(), nextPendingConnection()
+*/
+
+#include "private/qobject_p.h"
+#include "qalgorithms.h"
+#include "qhostaddress.h"
+#include "qlist.h"
+#include "qpointer.h"
+#include "qabstractsocketengine_p.h"
+#include "qtcpserver.h"
+#include "qtcpsocket.h"
+#include "qnetworkproxy.h"
+
+QT_BEGIN_NAMESPACE
+
+#define Q_CHECK_SOCKETENGINE(returnValue) do { \
+ if (!d->socketEngine) { \
+ return returnValue; \
+ } } while (0)
+
+class QTcpServerPrivate : public QObjectPrivate, public QAbstractSocketEngineReceiver
+{
+ Q_DECLARE_PUBLIC(QTcpServer)
+public:
+ QTcpServerPrivate();
+ ~QTcpServerPrivate();
+
+ QList<QTcpSocket *> pendingConnections;
+
+ quint16 port;
+ QHostAddress address;
+
+ QAbstractSocket::SocketState state;
+ QAbstractSocketEngine *socketEngine;
+
+ QAbstractSocket::SocketError serverSocketError;
+ QString serverSocketErrorString;
+
+ int maxConnections;
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy;
+ QNetworkProxy resolveProxy(const QHostAddress &address, quint16 port);
+#endif
+
+ // from QAbstractSocketEngineReceiver
+ void readNotification();
+ inline void writeNotification() {}
+ inline void exceptionNotification() {}
+ inline void connectionNotification() {}
+#ifndef QT_NO_NETWORKPROXY
+ inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *) {}
+#endif
+
+};
+
+/*! \internal
+*/
+QTcpServerPrivate::QTcpServerPrivate()
+ : port(0)
+ , state(QAbstractSocket::UnconnectedState)
+ , socketEngine(0)
+ , serverSocketError(QAbstractSocket::UnknownSocketError)
+ , maxConnections(30)
+{
+}
+
+/*! \internal
+*/
+QTcpServerPrivate::~QTcpServerPrivate()
+{
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*! \internal
+
+ Resolve the proxy to its final value.
+*/
+QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint16 port)
+{
+ if (address == QHostAddress::LocalHost ||
+ address == QHostAddress::LocalHostIPv6)
+ return QNetworkProxy::NoProxy;
+
+ QList<QNetworkProxy> proxies;
+ if (proxy.type() != QNetworkProxy::DefaultProxy) {
+ // a non-default proxy was set with setProxy
+ proxies << proxy;
+ } else {
+ // try the application settings instead
+ QNetworkProxyQuery query(port, QString(), QNetworkProxyQuery::TcpServer);
+ proxies = QNetworkProxyFactory::proxyForQuery(query);
+ }
+
+ // return the first that we can use
+ foreach (const QNetworkProxy &p, proxies) {
+ if (p.capabilities() & QNetworkProxy::ListeningCapability)
+ return p;
+ }
+
+ // no proxy found
+ // DefaultProxy will raise an error
+ return QNetworkProxy(QNetworkProxy::DefaultProxy);
+}
+#endif
+
+/*! \internal
+*/
+void QTcpServerPrivate::readNotification()
+{
+ Q_Q(QTcpServer);
+ for (;;) {
+ if (pendingConnections.count() >= maxConnections) {
+#if defined (QTCPSERVER_DEBUG)
+ qDebug("QTcpServerPrivate::_q_processIncomingConnection() too many connections");
+#endif
+ if (socketEngine->isReadNotificationEnabled())
+ socketEngine->setReadNotificationEnabled(false);
+ return;
+ }
+
+ int descriptor = socketEngine->accept();
+ if (descriptor == -1)
+ break;
+#if defined (QTCPSERVER_DEBUG)
+ qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor);
+#endif
+ q->incomingConnection(descriptor);
+
+ QPointer<QTcpServer> that = q;
+ emit q->newConnection();
+ if (!that || !q->isListening())
+ return;
+ }
+}
+
+/*!
+ Constructs a QTcpServer object.
+
+ \a parent is passed to the QObject constructor.
+
+ \sa listen(), setSocketDescriptor()
+*/
+QTcpServer::QTcpServer(QObject *parent)
+ : QObject(*new QTcpServerPrivate, parent)
+{
+}
+
+/*!
+ Destroys the QTcpServer object. If the server is listening for
+ connections, the socket is automatically closed.
+
+ Any client \l{QTcpSocket}s that are still connected must either
+ disconnect or be reparented before the server is deleted.
+
+ \sa close()
+*/
+QTcpServer::~QTcpServer()
+{
+ close();
+}
+
+/*!
+ Tells the server to listen for incoming connections on address \a
+ address and port \a port. If \a port is 0, a port is chosen
+ automatically. If \a address is QHostAddress::Any, the server
+ will listen on all network interfaces.
+
+ Returns true on success; otherwise returns false.
+
+ \sa isListening()
+*/
+bool QTcpServer::listen(const QHostAddress &address, quint16 port)
+{
+ Q_D(QTcpServer);
+ if (d->state == QAbstractSocket::ListeningState) {
+ qWarning("QTcpServer::listen() called when already listening");
+ return false;
+ }
+
+ QAbstractSocket::NetworkLayerProtocol proto = address.protocol();
+
+#ifdef QT_NO_NETWORKPROXY
+ static const QNetworkProxy &proxy = *(QNetworkProxy *)0;
+#else
+ QNetworkProxy proxy = d->resolveProxy(address, port);
+#endif
+
+ delete d->socketEngine;
+ d->socketEngine = QAbstractSocketEngine::createSocketEngine(QAbstractSocket::TcpSocket, proxy, 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(QAbstractSocket::TcpSocket, proto)) {
+ d->serverSocketError = d->socketEngine->error();
+ d->serverSocketErrorString = d->socketEngine->errorString();
+ return false;
+ }
+
+#if defined(Q_OS_UNIX)
+ // Under Unix, we want to be able to bind to the port, even if a socket on
+ // the same address-port is in TIME_WAIT. Under Windows this is possible
+ // anyway -- furthermore, the meaning of reusable on Windows is different:
+ // it means that you can use the same address-port for multiple listening
+ // sockets.
+ // Don't abort though if we can't set that option. For example the socks
+ // engine doesn't support that option, but that shouldn't prevent us from
+ // trying to bind/listen.
+ d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
+#endif
+
+ if (!d->socketEngine->bind(address, port)) {
+ d->serverSocketError = d->socketEngine->error();
+ d->serverSocketErrorString = d->socketEngine->errorString();
+ return false;
+ }
+
+ if (!d->socketEngine->listen()) {
+ d->serverSocketError = d->socketEngine->error();
+ d->serverSocketErrorString = d->socketEngine->errorString();
+ return false;
+ }
+
+ d->socketEngine->setReceiver(d);
+ d->socketEngine->setReadNotificationEnabled(true);
+
+ d->state = QAbstractSocket::ListeningState;
+ d->address = d->socketEngine->localAddress();
+ d->port = d->socketEngine->localPort();
+
+#if defined (QTCPSERVER_DEBUG)
+ qDebug("QTcpServer::listen(%i, \"%s\") == true (listening on port %i)", port,
+ address.toString().toLatin1().constData(), d->socketEngine->localPort());
+#endif
+ return true;
+}
+
+/*!
+ Returns true if the server is currently listening for incoming
+ connections; otherwise returns false.
+
+ \sa listen()
+*/
+bool QTcpServer::isListening() const
+{
+ Q_D(const QTcpServer);
+ Q_CHECK_SOCKETENGINE(false);
+ return d->socketEngine->state() == QAbstractSocket::ListeningState;
+}
+
+/*!
+ Closes the server. The server will no longer listen for incoming
+ connections.
+
+ \sa listen()
+*/
+void QTcpServer::close()
+{
+ Q_D(QTcpServer);
+
+ qDeleteAll(d->pendingConnections);
+ d->pendingConnections.clear();
+
+ if (d->socketEngine) {
+ d->socketEngine->close();
+ QT_TRY {
+ d->socketEngine->deleteLater();
+ } QT_CATCH(const std::bad_alloc &) {
+ // in out of memory situations, the socketEngine
+ // will be deleted in ~QTcpServer (it's a child-object of this)
+ }
+ d->socketEngine = 0;
+ }
+
+ d->state = QAbstractSocket::UnconnectedState;
+}
+
+/*!
+ Returns the native socket descriptor the server uses to listen
+ for incoming instructions, or -1 if the server is not listening.
+
+ If the server is using QNetworkProxy, the returned descriptor may
+ not be usable with native socket functions.
+
+ \sa setSocketDescriptor(), isListening()
+*/
+int QTcpServer::socketDescriptor() const
+{
+ Q_D(const QTcpServer);
+ Q_CHECK_SOCKETENGINE(-1);
+ return d->socketEngine->socketDescriptor();
+}
+
+/*!
+ Sets the socket descriptor this server should use when listening
+ for incoming connections to \a socketDescriptor. Returns true if
+ the socket is set successfully; otherwise returns false.
+
+ The socket is assumed to be in listening state.
+
+ \sa socketDescriptor(), isListening()
+*/
+bool QTcpServer::setSocketDescriptor(int socketDescriptor)
+{
+ Q_D(QTcpServer);
+ if (isListening()) {
+ qWarning("QTcpServer::setSocketDescriptor() called when already listening");
+ return false;
+ }
+
+ if (d->socketEngine)
+ delete d->socketEngine;
+ d->socketEngine = QAbstractSocketEngine::createSocketEngine(socketDescriptor, this);
+#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();
+#if defined (QTCPSERVER_DEBUG)
+ qDebug("QTcpServer::setSocketDescriptor(%i) failed (%s)", socketDescriptor,
+ d->serverSocketErrorString.toLatin1().constData());
+#endif
+ return false;
+ }
+
+ d->socketEngine->setReceiver(d);
+ d->socketEngine->setReadNotificationEnabled(true);
+
+ d->state = d->socketEngine->state();
+ d->address = d->socketEngine->localAddress();
+ d->port = d->socketEngine->localPort();
+
+#if defined (QTCPSERVER_DEBUG)
+ qDebug("QTcpServer::setSocketDescriptor(%i) succeeded.", socketDescriptor);
+#endif
+ return true;
+}
+
+/*!
+ Returns the server's port if the server is listening for
+ connections; otherwise returns 0.
+
+ \sa serverAddress(), listen()
+*/
+quint16 QTcpServer::serverPort() const
+{
+ Q_D(const QTcpServer);
+ Q_CHECK_SOCKETENGINE(0);
+ return d->socketEngine->localPort();
+}
+
+/*!
+ Returns the server's address if the server is listening for
+ connections; otherwise returns QHostAddress::Null.
+
+ \sa serverPort(), listen()
+*/
+QHostAddress QTcpServer::serverAddress() const
+{
+ Q_D(const QTcpServer);
+ Q_CHECK_SOCKETENGINE(QHostAddress(QHostAddress::Null));
+ return d->socketEngine->localAddress();
+}
+
+/*!
+ Waits for at most \a msec milliseconds or until an incoming
+ connection is available. Returns true if a connection is
+ available; otherwise returns false. If the operation timed out
+ and \a timedOut is not 0, *\a timedOut will be set to true.
+
+ This is a blocking function call. Its use is disadvised in a
+ single-threaded GUI application, since the whole application will
+ stop responding until the function returns.
+ waitForNewConnection() is mostly useful when there is no event
+ loop available.
+
+ The non-blocking alternative is to connect to the newConnection()
+ signal.
+
+ If msec is -1, this function will not time out.
+
+ \sa hasPendingConnections(), nextPendingConnection()
+*/
+bool QTcpServer::waitForNewConnection(int msec, bool *timedOut)
+{
+ Q_D(QTcpServer);
+ if (d->state != QAbstractSocket::ListeningState)
+ return false;
+
+ if (!d->socketEngine->waitForRead(msec, timedOut)) {
+ d->serverSocketError = d->socketEngine->error();
+ d->serverSocketErrorString = d->socketEngine->errorString();
+ return false;
+ }
+
+ if (timedOut && *timedOut)
+ return false;
+
+ d->readNotification();
+
+ return true;
+}
+
+/*!
+ Returns true if the server has a pending connection; otherwise
+ returns false.
+
+ \sa nextPendingConnection(), setMaxPendingConnections()
+*/
+bool QTcpServer::hasPendingConnections() const
+{
+ return !d_func()->pendingConnections.isEmpty();
+}
+
+/*!
+ Returns the next pending connection as a connected QTcpSocket
+ object.
+
+ The socket is created as a child of the server, which means that
+ it is automatically deleted when the QTcpServer object is
+ destroyed. It is still a good idea to delete the object
+ explicitly when you are done with it, to avoid wasting memory.
+
+ 0 is returned if this function is called when there are no pending
+ connections.
+
+ \note The returned QTcpSocket object cannot be used from another
+ thread. If you want to use an incoming connection from another thread,
+ you need to override incomingConnection().
+
+ \sa hasPendingConnections()
+*/
+QTcpSocket *QTcpServer::nextPendingConnection()
+{
+ Q_D(QTcpServer);
+ if (d->pendingConnections.isEmpty())
+ return 0;
+
+ if (!d->socketEngine->isReadNotificationEnabled())
+ d->socketEngine->setReadNotificationEnabled(true);
+
+ return d->pendingConnections.takeFirst();
+}
+
+/*!
+ This virtual function is called by QTcpServer when a new
+ connection is available. The \a socketDescriptor argument is the
+ native socket descriptor for the accepted connection.
+
+ The base implementation creates a QTcpSocket, sets the socket
+ descriptor and then stores the QTcpSocket in an internal list of
+ pending connections. Finally newConnection() is emitted.
+
+ Reimplement this function to alter the server's behavior when a
+ connection is available.
+
+ If this server is using QNetworkProxy then the \a socketDescriptor
+ may not be usable with native socket functions, and should only be
+ used with QTcpSocket::setSocketDescriptor().
+
+ \note If you want to handle an incoming connection as a new QTcpSocket
+ object in another thread you have to pass the socketDescriptor
+ to the other thread and create the QTcpSocket object there and
+ use its setSocketDescriptor() method.
+
+ \sa newConnection(), nextPendingConnection(), addPendingConnection()
+*/
+void QTcpServer::incomingConnection(int socketDescriptor)
+{
+#if defined (QTCPSERVER_DEBUG)
+ qDebug("QTcpServer::incomingConnection(%i)", socketDescriptor);
+#endif
+
+ QTcpSocket *socket = new QTcpSocket(this);
+ socket->setSocketDescriptor(socketDescriptor);
+ addPendingConnection(socket);
+}
+
+/*!
+ This function is called by QTcpServer::incomingConnection()
+ to add the \a socket to the list of pending incoming connections.
+
+ \note Don't forget to call this member from reimplemented
+ incomingConnection() if you do not want to break the
+ Pending Connections mechanism.
+
+ \sa incomingConnection()
+ \since 4.7
+*/
+void QTcpServer::addPendingConnection(QTcpSocket* socket)
+{
+ d_func()->pendingConnections.append(socket);
+}
+
+/*!
+ Sets the maximum number of pending accepted connections to \a
+ numConnections. QTcpServer will accept no more than \a
+ numConnections incoming connections before
+ nextPendingConnection() is called. By default, the limit is 30
+ pending connections.
+
+ Clients may still able to connect after the server has reached
+ its maximum number of pending connections (i.e., QTcpSocket can
+ still emit the connected() signal). QTcpServer will stop
+ accepting the new connections, but the operating system may
+ still keep them in queue.
+
+ \sa maxPendingConnections(), hasPendingConnections()
+*/
+void QTcpServer::setMaxPendingConnections(int numConnections)
+{
+ d_func()->maxConnections = numConnections;
+}
+
+/*!
+ Returns the maximum number of pending accepted connections. The
+ default is 30.
+
+ \sa setMaxPendingConnections(), hasPendingConnections()
+*/
+int QTcpServer::maxPendingConnections() const
+{
+ return d_func()->maxConnections;
+}
+
+/*!
+ Returns an error code for the last error that occurred.
+
+ \sa errorString()
+*/
+QAbstractSocket::SocketError QTcpServer::serverError() const
+{
+ return d_func()->serverSocketError;
+}
+
+/*!
+ Returns a human readable description of the last error that
+ occurred.
+
+ \sa serverError()
+*/
+QString QTcpServer::errorString() const
+{
+ return d_func()->serverSocketErrorString;
+}
+
+#ifndef QT_NO_NETWORKPROXY
+/*!
+ \since 4.1
+
+ Sets the explicit network proxy for this socket to \a networkProxy.
+
+ To disable the use of a proxy for this socket, use the
+ QNetworkProxy::NoProxy proxy type:
+
+ \snippet doc/src/snippets/code/src_network_socket_qtcpserver.cpp 0
+
+ \sa proxy(), QNetworkProxy
+*/
+void QTcpServer::setProxy(const QNetworkProxy &networkProxy)
+{
+ Q_D(QTcpServer);
+ d->proxy = networkProxy;
+}
+
+/*!
+ \since 4.1
+
+ Returns the network proxy for this socket.
+ By default QNetworkProxy::DefaultProxy is used.
+
+ \sa setProxy(), QNetworkProxy
+*/
+QNetworkProxy QTcpServer::proxy() const
+{
+ Q_D(const QTcpServer);
+ return d->proxy;
+}
+#endif // QT_NO_NETWORKPROXY
+
+QT_END_NAMESPACE
+
+#include "moc_qtcpserver.cpp"
+
diff --git a/src/network/socket/qtcpserver.h b/src/network/socket/qtcpserver.h
new file mode 100644
index 0000000000..4018da6d00
--- /dev/null
+++ b/src/network/socket/qtcpserver.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTCPSERVER_H
+#define QTCPSERVER_H
+
+#include <QtCore/qobject.h>
+#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qhostaddress.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QTcpServerPrivate;
+#ifndef QT_NO_NETWORKPROXY
+class QNetworkProxy;
+#endif
+class QTcpSocket;
+
+class Q_NETWORK_EXPORT QTcpServer : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QTcpServer(QObject *parent = 0);
+ virtual ~QTcpServer();
+
+ bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
+ void close();
+
+ bool isListening() const;
+
+ void setMaxPendingConnections(int numConnections);
+ int maxPendingConnections() const;
+
+ quint16 serverPort() const;
+ QHostAddress serverAddress() const;
+
+ int socketDescriptor() const;
+ bool setSocketDescriptor(int socketDescriptor);
+
+ bool waitForNewConnection(int msec = 0, bool *timedOut = 0);
+ virtual bool hasPendingConnections() const;
+ virtual QTcpSocket *nextPendingConnection();
+
+ QAbstractSocket::SocketError serverError() const;
+ QString errorString() const;
+
+#ifndef QT_NO_NETWORKPROXY
+ void setProxy(const QNetworkProxy &networkProxy);
+ QNetworkProxy proxy() const;
+#endif
+
+protected:
+ virtual void incomingConnection(int handle);
+ void addPendingConnection(QTcpSocket* socket);
+
+Q_SIGNALS:
+ void newConnection();
+
+private:
+ Q_DISABLE_COPY(QTcpServer)
+ Q_DECLARE_PRIVATE(QTcpServer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTCPSERVER_H
diff --git a/src/network/socket/qtcpsocket.cpp b/src/network/socket/qtcpsocket.cpp
new file mode 100644
index 0000000000..32edc2f8ab
--- /dev/null
+++ b/src/network/socket/qtcpsocket.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QTCPSOCKET_DEBUG
+
+/*!
+ \class QTcpSocket
+
+ \brief The QTcpSocket class provides a TCP socket.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ TCP (Transmission Control Protocol) is a reliable,
+ stream-oriented, connection-oriented transport protocol. It is
+ especially well suited for continuous transmission of data.
+
+ QTcpSocket is a convenience subclass of QAbstractSocket that
+ allows you to establish a TCP connection and transfer streams of
+ data. See the QAbstractSocket documentation for details.
+
+ \bold{Note:} TCP sockets cannot be opened in QIODevice::Unbuffered mode.
+
+ \section1 Symbian Platform Security Requirements
+
+ On Symbian, processes which use this class must have the
+ \c NetworkServices platform security capability. If the client
+ process lacks this capability, it will result in a panic.
+
+ Platform security capabilities are added via the
+ \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
+ qmake variable.
+
+ \sa QTcpServer, QUdpSocket, QFtp, QNetworkAccessManager,
+ {Fortune Server Example}, {Fortune Client Example},
+ {Threaded Fortune Server Example}, {Blocking Fortune Client Example},
+ {Loopback Example}, {Torrent Example}
+*/
+
+#include "qlist.h"
+#include "qtcpsocket_p.h"
+#include "qtcpsocket.h"
+#include "qhostaddress.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Creates a QTcpSocket object in state \c UnconnectedState.
+
+ \a parent is passed on to the QObject constructor.
+
+ \sa socketType()
+*/
+QTcpSocket::QTcpSocket(QObject *parent)
+ : QAbstractSocket(TcpSocket, *new QTcpSocketPrivate, parent)
+{
+#if defined(QTCPSOCKET_DEBUG)
+ qDebug("QTcpSocket::QTcpSocket()");
+#endif
+ d_func()->isBuffered = true;
+}
+
+/*!
+ Destroys the socket, closing the connection if necessary.
+
+ \sa close()
+*/
+
+QTcpSocket::~QTcpSocket()
+{
+#if defined(QTCPSOCKET_DEBUG)
+ qDebug("QTcpSocket::~QTcpSocket()");
+#endif
+}
+
+/*!
+ \internal
+*/
+QTcpSocket::QTcpSocket(QTcpSocketPrivate &dd, QObject *parent)
+ : QAbstractSocket(TcpSocket, dd, parent)
+{
+ d_func()->isBuffered = true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qtcpsocket.h b/src/network/socket/qtcpsocket.h
new file mode 100644
index 0000000000..a50e0feca9
--- /dev/null
+++ b/src/network/socket/qtcpsocket.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTCPSOCKET_H
+#define QTCPSOCKET_H
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+class QTcpSocketPrivate;
+
+class Q_NETWORK_EXPORT QTcpSocket : public QAbstractSocket
+{
+ Q_OBJECT
+public:
+ explicit QTcpSocket(QObject *parent = 0);
+ virtual ~QTcpSocket();
+
+protected:
+ QTcpSocket(QTcpSocketPrivate &dd, QObject *parent = 0);
+
+private:
+ Q_DISABLE_COPY(QTcpSocket)
+ Q_DECLARE_PRIVATE(QTcpSocket)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QTCPSOCKET_H
diff --git a/src/network/socket/qtcpsocket_p.h b/src/network/socket/qtcpsocket_p.h
new file mode 100644
index 0000000000..12414df2ed
--- /dev/null
+++ b/src/network/socket/qtcpsocket_p.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTCPSOCKET_P_H
+#define QTCPSOCKET_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/qtcpsocket.h>
+#include <private/qabstractsocket_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTcpSocketPrivate : public QAbstractSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QTcpSocket)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
new file mode 100644
index 0000000000..f8bcd1b967
--- /dev/null
+++ b/src/network/socket/qudpsocket.cpp
@@ -0,0 +1,567 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QUDPSOCKET_DEBUG
+
+/*! \class QUdpSocket
+
+ \reentrant
+ \brief The QUdpSocket class provides a UDP socket.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ UDP (User Datagram Protocol) is a lightweight, unreliable,
+ datagram-oriented, connectionless protocol. It can be used when
+ reliability isn't important. QUdpSocket is a subclass of
+ QAbstractSocket that allows you to send and receive UDP
+ datagrams.
+
+ The most common way to use this class is to bind to an address and port
+ using bind(), then call writeDatagram() and readDatagram() to transfer
+ data. If you want to use the standard QIODevice functions read(),
+ readLine(), write(), etc., you must first connect the socket directly to a
+ peer by calling connectToHost().
+
+ The socket emits the bytesWritten() signal every time a datagram
+ is written to the network. If you just want to send datagrams,
+ you don't need to call bind().
+
+ The readyRead() signal is emitted whenever datagrams arrive. In
+ that case, hasPendingDatagrams() returns true. Call
+ pendingDatagramSize() to obtain the size of the first pending
+ datagram, and readDatagram() to read it.
+
+ \note An incoming datagram should be read when you receive the readyRead()
+ signal, otherwise this signal will not be emitted for the next datagram.
+
+ Example:
+
+ \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},
+ \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
+
+ On Symbian, processes which use this class must have the
+ \c NetworkServices platform security capability. If the client
+ process lacks this capability, operations will result in a panic.
+
+ Platform security capabilities are added via the
+ \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
+ qmake variable.
+
+ \sa QTcpSocket
+*/
+
+/*! \enum QUdpSocket::BindFlag
+ \since 4.1
+
+ This enum describes the different flags you can pass to modify the
+ behavior of QUdpSocket::bind().
+
+ \note On Symbian OS bind flags behaviour depends on process capabilties.
+ If process has NetworkControl capability, the bind attempt with
+ ReuseAddressHint will always succeed even if the address and port is already
+ bound by another socket with any flags. If process does not have
+ NetworkControl capability, the bind attempt to address and port already
+ bound by another socket will always fail.
+
+ \value ShareAddress Allow other services to bind to the same address
+ and port. This is useful when multiple processes share
+ the load of a single service by listening to the same address and port
+ (e.g., a web server with several pre-forked listeners can greatly
+ improve response time). However, because any service is allowed to
+ rebind, this option is subject to certain security considerations.
+ Note that by combining this option with ReuseAddressHint, you will
+ also allow your service to rebind an existing shared address. On
+ Unix, this is equivalent to the SO_REUSEADDR socket option. On Windows,
+ this option is ignored.
+
+ \value DontShareAddress Bind the address and port exclusively, so that
+ no other services are allowed to rebind. By passing this option to
+ QUdpSocket::bind(), you are guaranteed that on successs, your service
+ is the only one that listens to the address and port. No services are
+ allowed to rebind, even if they pass ReuseAddressHint. This option
+ provides more security than ShareAddress, but on certain operating
+ systems, it requires you to run the server with administrator privileges.
+ On Unix and Mac OS X, not sharing is the default behavior for binding
+ an address and port, so this option is ignored. On Windows, this
+ option uses the SO_EXCLUSIVEADDRUSE socket option.
+
+ \value ReuseAddressHint Provides a hint to QUdpSocket that it should try
+ to rebind the service even if the address and port are already bound by
+ another socket. On Windows, this is equivalent to the SO_REUSEADDR
+ socket option. On Unix, this option is ignored.
+
+ \value DefaultForPlatform The default option for the current platform.
+ On Unix and Mac OS X, this is equivalent to (DontShareAddress
+ + ReuseAddressHint), and on Windows, its equivalent to ShareAddress.
+*/
+
+#include "qhostaddress.h"
+#include "qnetworkinterface.h"
+#include "qabstractsocket_p.h"
+#include "qudpsocket.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_UDPSOCKET
+
+#define QT_CHECK_BOUND(function, a) do { \
+ if (!isValid()) { \
+ qWarning(function" called on a QUdpSocket when not in QUdpSocket::BoundState"); \
+ return (a); \
+ } } while (0)
+
+class QUdpSocketPrivate : public QAbstractSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QUdpSocket)
+
+ bool doEnsureInitialized(const QHostAddress &bindAddress, quint16 bindPort,
+ const QHostAddress &remoteAddress);
+public:
+ inline bool ensureInitialized(const QHostAddress &bindAddress, quint16 bindPort)
+ { return doEnsureInitialized(bindAddress, bindPort, QHostAddress()); }
+
+ inline bool ensureInitialized(const QHostAddress &remoteAddress)
+ { return doEnsureInitialized(QHostAddress(), 0, remoteAddress); }
+};
+
+bool QUdpSocketPrivate::doEnsureInitialized(const QHostAddress &bindAddress, quint16 bindPort,
+ const QHostAddress &remoteAddress)
+{
+ const QHostAddress *address = &bindAddress;
+ QAbstractSocket::NetworkLayerProtocol proto = address->protocol();
+ if (proto == QUdpSocket::UnknownNetworkLayerProtocol) {
+ address = &remoteAddress;
+ proto = address->protocol();
+ }
+
+#if defined(QT_NO_IPV6)
+ Q_Q(QUdpSocket);
+ if (proto == QUdpSocket::IPv6Protocol) {
+ socketError = QUdpSocket::UnsupportedSocketOperationError;
+ q->setErrorString(QUdpSocket::tr("This platform does not support IPv6"));
+ return false;
+ }
+#endif
+
+ // now check if the socket engine is initialized and to the right type
+ if (!socketEngine || !socketEngine->isValid()) {
+ resolveProxy(remoteAddress.toString(), bindPort);
+ if (!initSocketLayer(address->protocol()))
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ Creates a QUdpSocket object.
+
+ \a parent is passed to the QObject constructor.
+
+ \sa socketType()
+*/
+QUdpSocket::QUdpSocket(QObject *parent)
+ : QAbstractSocket(UdpSocket, *new QUdpSocketPrivate, parent)
+{
+ d_func()->isBuffered = false;
+}
+
+/*!
+ Destroys the socket, closing the connection if necessary.
+
+ \sa close()
+*/
+QUdpSocket::~QUdpSocket()
+{
+}
+
+/*!
+ Binds this socket to the address \a address and the port \a port.
+ When bound, the signal readyRead() is emitted whenever a UDP
+ datagram arrives on the specified address and port. This function
+ is useful to write UDP servers.
+
+ On success, the functions returns true and the socket enters
+ BoundState; otherwise it returns false.
+
+ The socket is bound using the DefaultForPlatform BindMode.
+
+ \sa readDatagram()
+*/
+bool QUdpSocket::bind(const QHostAddress &address, quint16 port)
+{
+ Q_D(QUdpSocket);
+ if (!d->ensureInitialized(address, port))
+ return false;
+
+ bool result = d_func()->socketEngine->bind(address, port);
+ d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
+
+ if (!result) {
+ d->socketError = d_func()->socketEngine->error();
+ setErrorString(d_func()->socketEngine->errorString());
+ emit error(d_func()->socketError);
+ return false;
+ }
+
+ d->state = BoundState;
+ d->localAddress = d->socketEngine->localAddress();
+ d->localPort = d->socketEngine->localPort();
+
+ emit stateChanged(d_func()->state);
+ d_func()->socketEngine->setReadNotificationEnabled(true);
+ return true;
+}
+
+/*!
+ \since 4.1
+ \overload
+
+ Binds to \a address on port \a port, using the BindMode \a mode.
+*/
+bool QUdpSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
+{
+ Q_D(QUdpSocket);
+ if (!d->ensureInitialized(address, port))
+ return false;
+
+#ifdef Q_OS_UNIX
+ if ((mode & ShareAddress) || (mode & ReuseAddressHint))
+ d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
+ else
+ d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
+#endif
+#ifdef Q_OS_WIN
+ if (mode & ReuseAddressHint)
+ d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
+ else
+ d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
+ if (mode & DontShareAddress)
+ d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
+ else
+ d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
+#endif
+ bool result = d_func()->socketEngine->bind(address, port);
+ d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
+
+ if (!result) {
+ d->socketError = d_func()->socketEngine->error();
+ setErrorString(d_func()->socketEngine->errorString());
+ emit error(d_func()->socketError);
+ return false;
+ }
+
+ d->state = BoundState;
+ d->localAddress = d->socketEngine->localAddress();
+ d->localPort = d->socketEngine->localPort();
+
+ emit stateChanged(d_func()->state);
+ d_func()->socketEngine->setReadNotificationEnabled(true);
+ return true;
+}
+
+/*! \overload
+
+ Binds to QHostAddress:Any on port \a port.
+*/
+bool QUdpSocket::bind(quint16 port)
+{
+ return bind(QHostAddress::Any, port);
+}
+
+/*!
+ \since 4.1
+ \overload
+
+ Binds to QHostAddress:Any on port \a port, using the BindMode \a mode.
+*/
+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.
+
+ \sa pendingDatagramSize(), readDatagram()
+*/
+bool QUdpSocket::hasPendingDatagrams() const
+{
+ QT_CHECK_BOUND("QUdpSocket::hasPendingDatagrams()", false);
+ return d_func()->socketEngine->hasPendingDatagrams();
+}
+
+/*!
+ Returns the size of the first pending UDP datagram. If there is
+ no datagram available, this function returns -1.
+
+ \sa hasPendingDatagrams(), readDatagram()
+*/
+qint64 QUdpSocket::pendingDatagramSize() const
+{
+ QT_CHECK_BOUND("QUdpSocket::pendingDatagramSize()", -1);
+ return d_func()->socketEngine->pendingDatagramSize();
+}
+
+/*!
+ Sends the datagram at \a data of size \a size to the host
+ address \a address at port \a port. Returns the number of
+ bytes sent on success; otherwise returns -1.
+
+ Datagrams are always written as one block. The maximum size of a
+ datagram is highly platform-dependent, but can be as low as 8192
+ bytes. If the datagram is too large, this function will return -1
+ and error() will return DatagramTooLargeError.
+
+ Sending datagrams larger than 512 bytes is in general disadvised,
+ as even if they are sent successfully, they are likely to be
+ fragmented by the IP layer before arriving at their final
+ destination.
+
+ \warning In S60 5.0 and earlier versions, the writeDatagram return
+ value is not reliable for large datagrams.
+
+ \warning Calling this function on a connected UDP socket may
+ result in an error and no packet being sent. If you are using a
+ connected socket, use write() to send datagrams.
+
+ \sa readDatagram(), write()
+*/
+qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address,
+ quint16 port)
+{
+ Q_D(QUdpSocket);
+#if defined QUDPSOCKET_DEBUG
+ qDebug("QUdpSocket::writeDatagram(%p, %llu, \"%s\", %i)", data, size,
+ address.toString().toLatin1().constData(), port);
+#endif
+ if (!d->ensureInitialized(address))
+ return -1;
+
+ qint64 sent = d->socketEngine->writeDatagram(data, size, address, port);
+ d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
+
+ if (sent >= 0) {
+ emit bytesWritten(sent);
+ } else {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+ emit error(d->socketError);
+ }
+ return sent;
+}
+
+/*!
+ \fn qint64 QUdpSocket::writeDatagram(const QByteArray &datagram,
+ const QHostAddress &host, quint16 port)
+ \overload
+
+ Sends the datagram \a datagram to the host address \a host and at
+ port \a port.
+*/
+
+/*!
+ Receives a datagram no larger than \a maxSize bytes and stores
+ it in \a data. The sender's host address and port is stored in
+ *\a address and *\a port (unless the pointers are 0).
+
+ Returns the size of the datagram on success; otherwise returns
+ -1.
+
+ If \a maxSize is too small, the rest of the datagram will be
+ lost. To avoid loss of data, call pendingDatagramSize() to
+ determine the size of the pending datagram before attempting to
+ read it. If \a maxSize is 0, the datagram will be discarded.
+
+ \sa writeDatagram(), hasPendingDatagrams(), pendingDatagramSize()
+*/
+qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address,
+ quint16 *port)
+{
+ Q_D(QUdpSocket);
+
+#if defined QUDPSOCKET_DEBUG
+ qDebug("QUdpSocket::readDatagram(%p, %llu, %p, %p)", data, maxSize, address, port);
+#endif
+ QT_CHECK_BOUND("QUdpSocket::readDatagram()", -1);
+ qint64 readBytes = d->socketEngine->readDatagram(data, maxSize, address, port);
+ d_func()->socketEngine->setReadNotificationEnabled(true);
+ if (readBytes < 0) {
+ d->socketError = d->socketEngine->error();
+ setErrorString(d->socketEngine->errorString());
+ emit error(d->socketError);
+ }
+ return readBytes;
+}
+#endif // QT_NO_UDPSOCKET
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h
new file mode 100644
index 0000000000..7502349c7a
--- /dev/null
+++ b/src/network/socket/qudpsocket.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUDPSOCKET_H
+#define QUDPSOCKET_H
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qhostaddress.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_UDPSOCKET
+
+class QNetworkInterface;
+class QUdpSocketPrivate;
+
+class Q_NETWORK_EXPORT QUdpSocket : public QAbstractSocket
+{
+ Q_OBJECT
+public:
+ enum BindFlag {
+ DefaultForPlatform = 0x0,
+ ShareAddress = 0x1,
+ DontShareAddress = 0x2,
+ ReuseAddressHint = 0x4
+ };
+ Q_DECLARE_FLAGS(BindMode, BindFlag)
+
+ explicit QUdpSocket(QObject *parent = 0);
+ virtual ~QUdpSocket();
+
+ bool bind(const QHostAddress &address, quint16 port);
+ bool bind(quint16 port = 0);
+ bool bind(const QHostAddress &address, quint16 port, BindMode mode);
+ 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);
+ qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
+ inline qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
+ { return writeDatagram(datagram.constData(), datagram.size(), host, port); }
+
+private:
+ Q_DISABLE_COPY(QUdpSocket)
+ Q_DECLARE_PRIVATE(QUdpSocket)
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QUdpSocket::BindMode)
+
+#endif // QT_NO_UDPSOCKET
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QUDPSOCKET_H
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
new file mode 100644
index 0000000000..ac9001247a
--- /dev/null
+++ b/src/network/socket/socket.pri
@@ -0,0 +1,70 @@
+# Qt network socket
+
+HEADERS += socket/qabstractsocketengine_p.h \
+ socket/qhttpsocketengine_p.h \
+ socket/qsocks5socketengine_p.h \
+ socket/qabstractsocket.h \
+ socket/qabstractsocket_p.h \
+ socket/qtcpsocket.h \
+ socket/qudpsocket.h \
+ socket/qtcpserver.h \
+ socket/qlocalserver.h \
+ socket/qlocalserver_p.h \
+ socket/qlocalsocket.h \
+ socket/qlocalsocket_p.h
+
+SOURCES += socket/qabstractsocketengine.cpp \
+ socket/qhttpsocketengine.cpp \
+ socket/qsocks5socketengine.cpp \
+ socket/qabstractsocket.cpp \
+ socket/qtcpsocket.cpp \
+ socket/qudpsocket.cpp \
+ socket/qtcpserver.cpp \
+ socket/qlocalsocket.cpp \
+ socket/qlocalserver.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
+
+win32:SOURCES += socket/qnativesocketengine_win.cpp \
+ socket/qlocalsocket_win.cpp \
+ socket/qlocalserver_win.cpp
+
+wince*: {
+ SOURCES -= socket/qlocalsocket_win.cpp \
+ socket/qlocalserver_win.cpp
+ SOURCES += socket/qlocalsocket_tcp.cpp \
+ socket/qlocalserver_tcp.cpp
+
+ 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
new file mode 100644
index 0000000000..55942969e4
--- /dev/null
+++ b/src/network/ssl/qssl.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsslkey.h"
+
+QT_BEGIN_NAMESPACE
+
+/*! \namespace QSsl
+
+ \brief The QSsl namespace declares enums common to all SSL classes in QtNetwork.
+ \since 4.3
+
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+*/
+
+/*!
+ \enum QSsl::KeyType
+
+ Describes the two types of keys QSslKey supports.
+
+ \value PrivateKey A private key.
+ \value PublicKey A public key.
+*/
+
+/*!
+ \enum QSsl::KeyAlgorithm
+
+ Describes the different key algorithms supported by QSslKey.
+
+ \value Rsa The RSA algorithm.
+ \value Dsa The DSA algorithm.
+*/
+
+/*!
+ \enum QSsl::EncodingFormat
+
+ Describes supported encoding formats for certificates and keys.
+
+ \value Pem The PEM format.
+ \value Der The DER format.
+*/
+
+/*!
+ \enum QSsl::AlternateNameEntryType
+
+ Describes the key types for alternate name entries in QSslCertificate.
+
+ \value EmailEntry An email entry; the entry contains an email address that
+ the certificate is valid for.
+
+ \value DnsEntry A DNS host name entry; the entry contains a host name
+ entry that the certificate is valid for. The entry may contain wildcards.
+
+ \sa QSslCertificate::alternateSubjectNames()
+
+*/
+
+/*!
+ \enum QSsl::SslProtocol
+
+ Describes the protocol of the cipher.
+
+ \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
+ reasons. However, SSL and TLS are not compatible with each other:
+ if you get unexpected handshake failures, verify that you chose
+ the correct setting for your protocol.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
new file mode 100644
index 0000000000..24dbb09747
--- /dev/null
+++ b/src/network/ssl/qssl.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSL_H
+#define QSSL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+namespace QSsl {
+ enum KeyType {
+ PrivateKey,
+ PublicKey
+ };
+
+ enum EncodingFormat {
+ Pem,
+ Der
+ };
+
+ enum KeyAlgorithm {
+ Rsa,
+ Dsa
+ };
+
+ enum AlternateNameEntryType {
+ EmailEntry,
+ DnsEntry
+ };
+
+ enum SslProtocol {
+ SslV3,
+ SslV2,
+ TlsV1, // ### Qt 5: rename to TlsV1_0 or so
+ AnyProtocol,
+ TlsV1SslV3,
+ SecureProtocols,
+ UnknownProtocol = -1
+ };
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSSL_H
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
new file mode 100644
index 0000000000..a5cdf011aa
--- /dev/null
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -0,0 +1,858 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QSslCertificate
+ \brief The QSslCertificate class provides a convenient API for an X509 certificate.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ QSslCertificate stores an X509 certificate, and is commonly used
+ to verify the identity and store information about the local host,
+ a remotely connected peer, or a trusted third party Certificate
+ Authority.
+
+ There are many ways to construct a QSslCertificate. The most
+ common way is to call QSslSocket::peerCertificate(), which returns
+ a QSslCertificate object, or QSslSocket::peerCertificateChain(),
+ which returns a list of them. You can also load certificates from
+ a DER (binary) or PEM (Base64) encoded bundle, typically stored as
+ one or more local files, or in a Qt Resource.
+
+ You can call isNull() to check if your certificate is null. By
+ default, QSslCertificate constructs a null certificate. To check
+ if the certificate is valid, call isValid(). A null certificate is
+ invalid, but an invalid certificate is not necessarily null. If
+ you want to reset all contents in a certificate, call clear().
+
+ After loading a certificate, you can find information about the
+ certificate, its subject, and its issuer, by calling one of the
+ many accessor functions, including version(), serialNumber(),
+ issuerInfo() and subjectInfo(). You can call effectiveDate() and
+ expiryDate() to check when the certificate starts being
+ effective and when it expires.
+ The publicKey() function returns the certificate
+ subject's public key as a QSslKey. You can call issuerInfo() or
+ subjectInfo() to get detailed information about the certificate
+ issuer and its subject.
+
+ Internally, QSslCertificate is stored as an X509 structure. You
+ can access this handle by calling handle(), but the results are
+ likely to not be portable.
+
+ \sa QSslSocket, QSslKey, QSslCipher, QSslError
+*/
+
+/*!
+ \enum QSslCertificate::SubjectInfo
+
+ Describes keys that you can pass to QSslCertificate::issuerInfo() or
+ QSslCertificate::subjectInfo() to get information about the certificate
+ issuer or subject.
+
+ \value Organization "O" The name of the organization.
+
+ \value CommonName "CN" The common name; most often this is used to store
+ the host name.
+
+ \value LocalityName "L" The locality.
+
+ \value OrganizationalUnitName "OU" The organizational unit name.
+
+ \value CountryName "C" The country.
+
+ \value StateOrProvinceName "ST" The state or province.
+*/
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qsslcertificate.h"
+#include "qsslcertificate_p.h"
+#include "qsslkey.h"
+#include "qsslkey_p.h"
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+// forward declaration
+static QMap<QString, QString> _q_mapFromOnelineName(char *name);
+
+/*!
+ Constructs a QSslCertificate by reading \a format encoded data
+ from \a device and using the first certificate found. You can
+ later call isNull() to see if \a device contained a certificate,
+ and if this certificate was loaded successfully.
+*/
+QSslCertificate::QSslCertificate(QIODevice *device, QSsl::EncodingFormat format)
+ : d(new QSslCertificatePrivate)
+{
+ QSslSocketPrivate::ensureInitialized();
+ if (device)
+ d->init(device->readAll(), format);
+}
+
+/*!
+ Constructs a QSslCertificate by parsing the \a format encoded
+ \a data and using the first available certificate found. You can
+ later call isNull() to see if \a data contained a certificate,
+ and if this certificate was loaded successfully.
+*/
+QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat format)
+ : d(new QSslCertificatePrivate)
+{
+ QSslSocketPrivate::ensureInitialized();
+ d->init(data, format);
+}
+
+/*!
+ Constructs an identical copy of \a other.
+*/
+QSslCertificate::QSslCertificate(const QSslCertificate &other) : d(other.d)
+{
+}
+
+/*!
+ Destroys the QSslCertificate.
+*/
+QSslCertificate::~QSslCertificate()
+{
+}
+
+/*!
+ Copies the contents of \a other into this certificate, making the two
+ certificates identical.
+*/
+QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this certificate is the same as \a other; otherwise
+ returns false.
+*/
+bool QSslCertificate::operator==(const QSslCertificate &other) const
+{
+ if (d == other.d)
+ return true;
+ if (d->null && other.d->null)
+ return true;
+ if (d->x509 && other.d->x509)
+ return q_X509_cmp(d->x509, other.d->x509) == 0;
+ return false;
+}
+
+/*!
+ \fn bool QSslCertificate::operator!=(const QSslCertificate &other) const
+
+ Returns true if this certificate is not the same as \a other; otherwise
+ returns false.
+*/
+
+/*!
+ Returns true if this is a null certificate (i.e., a certificate
+ with no contents); otherwise returns false.
+
+ By default, QSslCertificate constructs a null certificate.
+
+ \sa isValid(), clear()
+*/
+bool QSslCertificate::isNull() const
+{
+ return d->null;
+}
+
+/*!
+ Returns true if this certificate is valid; otherwise returns
+ false.
+
+ Note: Currently, this function checks that the current
+ data-time is within the date-time range during which the
+ certificate is considered valid, and checks that the
+ certificate is not in a blacklist of fraudulent certificates.
+
+ \sa isNull()
+*/
+bool QSslCertificate::isValid() const
+{
+ const QDateTime currentTime = QDateTime::currentDateTime();
+ return currentTime >= d->notValidBefore &&
+ currentTime <= d->notValidAfter &&
+ ! QSslCertificatePrivate::isBlacklisted(*this);
+}
+
+/*!
+ Clears the contents of this certificate, making it a null
+ certificate.
+
+ \sa isNull()
+*/
+void QSslCertificate::clear()
+{
+ if (isNull())
+ return;
+ d = new QSslCertificatePrivate;
+}
+
+/*!
+ Returns the certificate's version string.
+*/
+QByteArray QSslCertificate::version() const
+{
+ if (d->versionString.isEmpty() && d->x509)
+ d->versionString =
+ QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1);
+
+ return d->versionString;
+}
+
+/*!
+ Returns the certificate's serial number string in decimal format.
+ In case the serial number cannot be converted to decimal format
+ (i.e. if it is bigger than 4294967295, which means it does not fit into 4 bytes),
+ its hexadecimal version is returned.
+*/
+QByteArray QSslCertificate::serialNumber() const
+{
+ if (d->serialNumberString.isEmpty() && d->x509) {
+ ASN1_INTEGER *serialNumber = d->x509->cert_info->serialNumber;
+ // if we cannot convert to a long, just output the hexadecimal number
+ if (serialNumber->length > 4) {
+ QByteArray hexString;
+ hexString.reserve(serialNumber->length * 3);
+ for (int a = 0; a < serialNumber->length; ++a) {
+ hexString += QByteArray::number(serialNumber->data[a], 16).rightJustified(2, '0');
+ hexString += ':';
+ }
+ hexString.chop(1);
+ d->serialNumberString = hexString;
+ } else {
+ d->serialNumberString = QByteArray::number(qlonglong(q_ASN1_INTEGER_get(serialNumber)));
+ }
+ }
+ return d->serialNumberString;
+}
+
+/*!
+ Returns a cryptographic digest of this certificate. By default,
+ an MD5 digest will be generated, but you can also specify a
+ custom \a algorithm.
+*/
+QByteArray QSslCertificate::digest(QCryptographicHash::Algorithm algorithm) const
+{
+ return QCryptographicHash::hash(toDer(), algorithm);
+}
+
+static QString _q_SubjectInfoToString(QSslCertificate::SubjectInfo info)
+{
+ QString str;
+ switch (info) {
+ case QSslCertificate::Organization: str = QLatin1String("O"); break;
+ case QSslCertificate::CommonName: str = QLatin1String("CN"); break;
+ case QSslCertificate::LocalityName: str = QLatin1String("L"); break;
+ case QSslCertificate::OrganizationalUnitName: str = QLatin1String("OU"); break;
+ case QSslCertificate::CountryName: str = QLatin1String("C"); break;
+ case QSslCertificate::StateOrProvinceName: str = QLatin1String("ST"); break;
+ }
+ return str;
+}
+
+/*!
+ \fn QString QSslCertificate::issuerInfo(SubjectInfo subject) const
+
+ Returns the issuer information for the \a subject from the
+ certificate, or an empty string if there is no information for
+ \a subject in the certificate.
+
+ \sa subjectInfo()
+*/
+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));
+
+ return d->issuerInfo.value(_q_SubjectInfoToString(info));
+}
+
+/*!
+ Returns the issuer information for \a tag from the certificate,
+ or an empty string if there is no information for \a tag in the
+ certificate.
+
+ \sa subjectInfo()
+*/
+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));
+
+ return d->issuerInfo.value(QString::fromLatin1(tag));
+}
+
+/*!
+
+ \fn QString QSslCertificate::subjectInfo(SubjectInfo subject) const
+
+ Returns the information for the \a subject, or an empty string if
+ there is no information for \a subject in the certificate.
+
+ \sa issuerInfo()
+*/
+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));
+
+ return d->subjectInfo.value(_q_SubjectInfoToString(info));
+}
+
+/*!
+ Returns the subject information for \a tag, or an empty string if
+ there is no information for \a tag in the certificate.
+
+ \sa issuerInfo()
+*/
+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));
+
+ return d->subjectInfo.value(QString::fromLatin1(tag));
+}
+
+/*!
+ Returns the list of alternative subject names for this
+ certificate. The alternate subject names typically contain host
+ names, optionally with wildcards, that are valid for this
+ certificate.
+
+ These names are tested against the connected peer's host name, if
+ either the subject information for \l CommonName doesn't define a
+ valid host name, or the subject info name doesn't match the peer's
+ host name.
+
+ \sa subjectInfo()
+*/
+QMultiMap<QSsl::AlternateNameEntryType, QString> QSslCertificate::alternateSubjectNames() const
+{
+ QMultiMap<QSsl::AlternateNameEntryType, QString> result;
+
+ if (!d->x509)
+ return result;
+
+ STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME)*)q_X509_get_ext_d2i(d->x509, NID_subject_alt_name, 0, 0);
+
+ if (altNames) {
+ for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) {
+ const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i);
+ if (genName->type != GEN_DNS && genName->type != GEN_EMAIL)
+ continue;
+
+ int len = q_ASN1_STRING_length(genName->d.ia5);
+ if (len < 0 || len >= 8192) {
+ // broken name
+ continue;
+ }
+
+ const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_data(genName->d.ia5));
+ const QString altName = QString::fromLatin1(altNameStr, len);
+ if (genName->type == GEN_DNS)
+ result.insert(QSsl::DnsEntry, altName);
+ else if (genName->type == GEN_EMAIL)
+ result.insert(QSsl::EmailEntry, altName);
+ }
+ q_sk_pop_free((STACK*)altNames, reinterpret_cast<void(*)(void*)>(q_sk_free));
+ }
+
+ return result;
+}
+
+/*!
+ Returns the date-time that the certificate becomes valid, or an
+ empty QDateTime if this is a null certificate.
+
+ \sa expiryDate()
+*/
+QDateTime QSslCertificate::effectiveDate() const
+{
+ return d->notValidBefore;
+}
+
+/*!
+ Returns the date-time that the certificate expires, or an empty
+ QDateTime if this is a null certificate.
+
+ \sa effectiveDate()
+*/
+QDateTime QSslCertificate::expiryDate() const
+{
+ return d->notValidAfter;
+}
+
+/*!
+ Returns a pointer to the native certificate handle, if there is
+ one, or a null pointer otherwise.
+
+ You can use this handle, together with the native API, to access
+ extended information about the certificate.
+
+ \warning Use of this function has a high probability of being
+ non-portable, and its return value may vary from platform to
+ platform or change from minor release to minor release.
+*/
+Qt::HANDLE QSslCertificate::handle() const
+{
+ return Qt::HANDLE(d->x509);
+}
+
+/*!
+ Returns the certificate subject's public key.
+*/
+QSslKey QSslCertificate::publicKey() const
+{
+ if (!d->x509)
+ return QSslKey();
+
+ QSslKey key;
+
+ key.d->type = QSsl::PublicKey;
+ X509_PUBKEY *xkey = d->x509->cert_info->key;
+ EVP_PKEY *pkey = q_X509_PUBKEY_get(xkey);
+ Q_ASSERT(pkey);
+
+ if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA) {
+ key.d->rsa = q_EVP_PKEY_get1_RSA(pkey);
+ key.d->algorithm = QSsl::Rsa;
+ key.d->isNull = false;
+ } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
+ key.d->dsa = q_EVP_PKEY_get1_DSA(pkey);
+ key.d->algorithm = QSsl::Dsa;
+ key.d->isNull = false;
+ } else if (q_EVP_PKEY_type(pkey->type) == EVP_PKEY_DH) {
+ // DH unsupported
+ } else {
+ // error?
+ }
+
+ q_EVP_PKEY_free(pkey);
+ return key;
+}
+
+/*!
+ Returns this certificate converted to a PEM (Base64) encoded
+ representation.
+*/
+QByteArray QSslCertificate::toPem() const
+{
+ if (!d->x509)
+ return QByteArray();
+ return d->QByteArray_from_X509(d->x509, QSsl::Pem);
+}
+
+/*!
+ Returns this certificate converted to a DER (binary) encoded
+ representation.
+*/
+QByteArray QSslCertificate::toDer() const
+{
+ if (!d->x509)
+ return QByteArray();
+ return d->QByteArray_from_X509(d->x509, QSsl::Der);
+}
+
+/*!
+ Searches all files in the \a path for certificates encoded in the
+ specified \a format and returns them in a list. \e must be a file or a
+ pattern matching one or more files, as specified by \a syntax.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslcertificate.cpp 0
+
+ \sa fromData()
+*/
+QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
+ QSsl::EncodingFormat format,
+ QRegExp::PatternSyntax syntax)
+{
+ // $, (,), *, +, ., ?, [, ,], ^, {, | and }.
+ int pos = -1;
+ if (syntax == QRegExp::Wildcard)
+ pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\*\\?\\[\\]]")));
+ else if (syntax != QRegExp::FixedString)
+ pos = path.indexOf(QRegExp(QLatin1String("[^\\][\\$\\(\\)\\*\\+\\.\\?\\[\\]\\^\\{\\}\\|]")));
+ QString pathPrefix = path.left(pos); // == path if pos < 0
+ if (pos != -1)
+ pathPrefix = pathPrefix.left(pathPrefix.lastIndexOf(QLatin1Char('/')));
+
+ // Special case - if the prefix ends up being nothing, use "." instead and
+ // chop off the first two characters from the glob'ed paths.
+ int startIndex = 0;
+ if (pathPrefix.trimmed().isEmpty()) {
+ if(path.startsWith(QLatin1Char('/'))) {
+ pathPrefix = path.left(path.indexOf(QRegExp(QLatin1String("[\\*\\?\\[]"))));
+ pathPrefix = path.left(path.lastIndexOf(QLatin1Char('/')));
+ } else {
+ startIndex = 2;
+ pathPrefix = QLatin1String(".");
+ }
+ }
+
+ // The path is a file.
+ if (pos == -1 && QFileInfo(pathPrefix).isFile()) {
+ QFile file(pathPrefix);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text))
+ return QSslCertificate::fromData(file.readAll(),format);
+ return QList<QSslCertificate>();
+ }
+
+ // The path can be a file or directory.
+ QList<QSslCertificate> certs;
+ QRegExp pattern(path, Qt::CaseSensitive, syntax);
+ QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
+ if (!pattern.exactMatch(filePath))
+ continue;
+
+ QFile file(filePath);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text))
+ certs += QSslCertificate::fromData(file.readAll(),format);
+ }
+ return certs;
+}
+
+/*!
+ Searches for and parses all certificates in \a device that are
+ encoded in the specified \a format and returns them in a list of
+ certificates.
+
+ \sa fromData()
+*/
+QList<QSslCertificate> QSslCertificate::fromDevice(QIODevice *device, QSsl::EncodingFormat format)
+{
+ if (!device) {
+ qWarning("QSslCertificate::fromDevice: cannot read from a null device");
+ return QList<QSslCertificate>();
+ }
+ return fromData(device->readAll(), format);
+}
+
+/*!
+ Searches for and parses all certificates in \a data that are
+ encoded in the specified \a format and returns them in a list of
+ certificates.
+
+ \sa fromDevice()
+*/
+QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::EncodingFormat format)
+{
+ return (format == QSsl::Pem)
+ ? QSslCertificatePrivate::certificatesFromPem(data)
+ : QSslCertificatePrivate::certificatesFromDer(data);
+}
+
+void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format)
+{
+ if (!data.isEmpty()) {
+ QList<QSslCertificate> certs = (format == QSsl::Pem)
+ ? certificatesFromPem(data, 1)
+ : certificatesFromDer(data, 1);
+ if (!certs.isEmpty()) {
+ *this = *certs.first().d;
+ if (x509)
+ x509 = q_X509_dup(x509);
+ }
+ }
+}
+
+#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
+#define ENDCERTSTRING "-----END CERTIFICATE-----"
+
+// ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations)
+QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format)
+{
+ if (!x509) {
+ qWarning("QSslSocketBackendPrivate::X509_to_QByteArray: null X509");
+ return QByteArray();
+ }
+
+ // Use i2d_X509 to convert the X509 to an array.
+ int length = q_i2d_X509(x509, 0);
+ QByteArray array;
+ array.resize(length);
+ char *data = array.data();
+ char **dataP = &data;
+ unsigned char **dataPu = (unsigned char **)dataP;
+ if (q_i2d_X509(x509, dataPu) < 0)
+ return QByteArray();
+
+ if (format == QSsl::Der)
+ return array;
+
+ // Convert to Base64 - wrap at 64 characters.
+ array = array.toBase64();
+ QByteArray tmp;
+ for (int i = 0; i <= array.size() - 64; i += 64) {
+ tmp += QByteArray::fromRawData(array.data() + i, 64);
+ tmp += '\n';
+ }
+ if (int remainder = array.size() % 64) {
+ tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder);
+ tmp += '\n';
+ }
+
+ return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n";
+}
+
+static QMap<QString, QString> _q_mapFromOnelineName(char *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();
+ }
+
+ return info;
+}
+
+QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509)
+{
+ QSslCertificate certificate;
+ if (!x509 || !QSslSocket::supportsSsl())
+ return certificate;
+
+ ASN1_TIME *nbef = q_X509_get_notBefore(x509);
+ ASN1_TIME *naft = q_X509_get_notAfter(x509);
+ certificate.d->notValidBefore = q_getTimeFromASN1(nbef);
+ certificate.d->notValidAfter = q_getTimeFromASN1(naft);
+ certificate.d->null = false;
+ certificate.d->x509 = q_X509_dup(x509);
+
+ return certificate;
+}
+
+static bool matchLineFeed(const QByteArray &pem, int *offset)
+{
+ char ch = 0;
+
+ // ignore extra whitespace at the end of the line
+ while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ')
+ ++*offset;
+
+ if (ch == '\n') {
+ *offset += 1;
+ return true;
+ }
+ if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') {
+ *offset += 2;
+ return true;
+ }
+ return false;
+}
+
+QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count)
+{
+ QList<QSslCertificate> certificates;
+ QSslSocketPrivate::ensureInitialized();
+
+ int offset = 0;
+ while (count == -1 || certificates.size() < count) {
+ int startPos = pem.indexOf(BEGINCERTSTRING, offset);
+ if (startPos == -1)
+ break;
+ startPos += sizeof(BEGINCERTSTRING) - 1;
+ if (!matchLineFeed(pem, &startPos))
+ break;
+
+ int endPos = pem.indexOf(ENDCERTSTRING, startPos);
+ if (endPos == -1)
+ break;
+
+ offset = endPos + sizeof(ENDCERTSTRING) - 1;
+ if (offset < pem.size() && !matchLineFeed(pem, &offset))
+ break;
+
+ QByteArray decoded = QByteArray::fromBase64(
+ QByteArray::fromRawData(pem.data() + startPos, endPos - startPos));
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+ const unsigned char *data = (const unsigned char *)decoded.data();
+#else
+ unsigned char *data = (unsigned char *)decoded.data();
+#endif
+
+ if (X509 *x509 = q_d2i_X509(0, &data, decoded.size())) {
+ certificates << QSslCertificate_from_X509(x509);
+ q_X509_free(x509);
+ }
+ }
+
+ return certificates;
+}
+
+QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count)
+{
+ QList<QSslCertificate> certificates;
+ QSslSocketPrivate::ensureInitialized();
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+ const unsigned char *data = (const unsigned char *)der.data();
+#else
+ unsigned char *data = (unsigned char *)der.data();
+#endif
+ int size = der.size();
+
+ while (count == -1 || certificates.size() < count) {
+ if (X509 *x509 = q_d2i_X509(0, &data, size)) {
+ certificates << QSslCertificate_from_X509(x509);
+ q_X509_free(x509);
+ } else {
+ break;
+ }
+ size -= ((char *)data - der.data());
+ }
+
+ return certificates;
+}
+
+// These certificates are known to be fraudulent and were created during the comodo
+// compromise. See http://www.comodo.com/Comodo-Fraud-Incident-2011-03-23.html
+static const char *certificate_blacklist[] = {
+ "04:7e:cb:e9:fc:a5:5f:7b:d0:9e:ae:36:e1:0c:ae:1e",
+ "f5:c8:6a:f3:61:62:f1:3a:64:f5:4f:6d:c9:58:7c:06",
+ "d7:55:8f:da:f5:f1:10:5b:b2:13:28:2b:70:77:29:a3",
+ "39:2a:43:4f:0e:07:df:1f:8a:a3:05:de:34:e0:c2:29",
+ "3e:75:ce:d4:6b:69:30:21:21:88:30:ae:86:a8:2a:71",
+ "e9:02:8b:95:78:e4:15:dc:1a:71:0a:2b:88:15:44:47",
+ "92:39:d5:34:8f:40:d1:69:5a:74:54:70:e1:f2:3f:43",
+ "b0:b7:13:3e:d0:96:f9:b5:6f:ae:91:c8:74:bd:3a:c0",
+ "d8:f3:5f:4e:b7:87:2b:2d:ab:06:92:e3:15:38:2f:b0",
+ 0
+};
+
+bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
+{
+ for (int a = 0; certificate_blacklist[a] != 0; a++) {
+ if (certificate.serialNumber() == certificate_blacklist[a])
+ return true;
+ }
+ return false;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
+{
+ debug << "QSslCertificate("
+ << certificate.version()
+ << ',' << certificate.serialNumber()
+ << ',' << certificate.digest().toBase64()
+ << ',' << certificate.issuerInfo(QSslCertificate::Organization)
+ << ',' << certificate.subjectInfo(QSslCertificate::Organization)
+ << ',' << certificate.alternateSubjectNames()
+#ifndef QT_NO_TEXTSTREAM
+ << ',' << certificate.effectiveDate()
+ << ',' << certificate.expiryDate()
+#endif
+ << ')';
+ return debug;
+}
+QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info)
+{
+ switch (info) {
+ case QSslCertificate::Organization: debug << "Organization"; break;
+ case QSslCertificate::CommonName: debug << "CommonName"; break;
+ case QSslCertificate::CountryName: debug << "CountryName"; break;
+ case QSslCertificate::LocalityName: debug << "LocalityName"; break;
+ case QSslCertificate::OrganizationalUnitName: debug << "OrganizationalUnitName"; break;
+ case QSslCertificate::StateOrProvinceName: debug << "StateOrProvinceName"; break;
+ }
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
new file mode 100644
index 0000000000..e972ee7239
--- /dev/null
+++ b/src/network/ssl/qsslcertificate.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLCERTIFICATE_H
+#define QSSLCERTIFICATE_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qregexp.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtNetwork/qssl.h>
+
+typedef struct x509_st X509; // ### check if this works
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+class QDateTime;
+class QIODevice;
+class QSslKey;
+class QStringList;
+template <typename T, typename U> class QMultiMap;
+
+class QSslCertificatePrivate;
+class Q_NETWORK_EXPORT QSslCertificate
+{
+public:
+ enum SubjectInfo {
+ Organization,
+ CommonName,
+ LocalityName,
+ OrganizationalUnitName,
+ CountryName,
+ StateOrProvinceName
+ };
+
+ QSslCertificate(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
+ QSslCertificate( // ### s/encoded/data (to be consistent with signature in .cpp file) ?
+ const QByteArray &encoded = QByteArray(), QSsl::EncodingFormat format = QSsl::Pem);
+ QSslCertificate(const QSslCertificate &other);
+ ~QSslCertificate();
+ QSslCertificate &operator=(const QSslCertificate &other);
+ bool operator==(const QSslCertificate &other) const;
+ inline bool operator!=(const QSslCertificate &other) const { return !operator==(other); }
+
+ bool isNull() const;
+ bool isValid() const;
+ void clear();
+
+ // Certificate info
+ QByteArray version() const;
+ QByteArray serialNumber() const;
+ QByteArray digest(QCryptographicHash::Algorithm algorithm = QCryptographicHash::Md5) const;
+ QString issuerInfo(SubjectInfo info) const;
+ QString issuerInfo(const QByteArray &tag) const;
+ QString subjectInfo(SubjectInfo info) const;
+ QString subjectInfo(const QByteArray &tag) const;
+ QMultiMap<QSsl::AlternateNameEntryType, QString> alternateSubjectNames() const;
+ QDateTime effectiveDate() const;
+ QDateTime expiryDate() const;
+ QSslKey publicKey() const;
+
+ QByteArray toPem() const;
+ QByteArray toDer() const;
+
+ static QList<QSslCertificate> fromPath(
+ const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ static QList<QSslCertificate> fromDevice(
+ QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
+ static QList<QSslCertificate> fromData(
+ const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem);
+
+ Qt::HANDLE handle() const;
+
+private:
+ QExplicitlySharedDataPointer<QSslCertificatePrivate> d;
+ friend class QSslCertificatePrivate;
+ friend class QSslSocketBackendPrivate;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCertificate &certificate);
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, QSslCertificate::SubjectInfo info);
+#endif
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
new file mode 100644
index 0000000000..1ce33d3bfd
--- /dev/null
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLCERTIFICATE_P_H
+#define QSSLCERTIFICATE_P_H
+
+#include "qsslcertificate.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 "qsslsocket_p.h"
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmap.h>
+
+#include <openssl/x509.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSslCertificatePrivate
+{
+public:
+ QSslCertificatePrivate()
+ : null(true), x509(0)
+ {
+ QSslSocketPrivate::ensureInitialized();
+ }
+
+ ~QSslCertificatePrivate()
+ {
+ if (x509)
+ q_X509_free(x509);
+ }
+
+ bool null;
+ QByteArray versionString;
+ QByteArray serialNumberString;
+
+ QMap<QString, QString> issuerInfo;
+ QMap<QString, QString> subjectInfo;
+ QDateTime notValidAfter;
+ QDateTime notValidBefore;
+
+ X509 *x509;
+
+ void init(const QByteArray &data, QSsl::EncodingFormat format);
+
+ static QByteArray QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format);
+ static QSslCertificate QSslCertificate_from_X509(X509 *x509);
+ static QList<QSslCertificate> certificatesFromPem(const QByteArray &pem, int count = -1);
+ static QList<QSslCertificate> certificatesFromDer(const QByteArray &der, int count = -1);
+ static bool isBlacklisted(const QSslCertificate &certificate);
+
+ friend class QSslSocketBackendPrivate;
+
+ QAtomicInt ref;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qsslcipher.cpp b/src/network/ssl/qsslcipher.cpp
new file mode 100644
index 0000000000..33d4b66a50
--- /dev/null
+++ b/src/network/ssl/qsslcipher.cpp
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QSslCipher
+ \brief The QSslCipher class represents an SSL cryptographic cipher.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ QSslCipher stores information about one cryptographic cipher. It
+ is most commonly used with QSslSocket, either for configuring
+ which ciphers the socket can use, or for displaying the socket's
+ ciphers to the user.
+
+ \sa QSslSocket, QSslKey
+*/
+
+#include "qsslcipher.h"
+#include "qsslcipher_p.h"
+#include "qsslsocket.h"
+
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Constructs an empty QSslCipher object.
+*/
+QSslCipher::QSslCipher()
+ : d(new QSslCipherPrivate)
+{
+}
+
+/*!
+ Constructs a QSslCipher object for the cipher determined by \a
+ name and \a protocol. The constructor accepts only supported
+ ciphers (i.e., the \a name and \a protocol must identify a cipher
+ in the list of ciphers returned by
+ QSslSocket::supportedCiphers()).
+
+ You can call isNull() after construction to check if \a name and
+ \a protocol correctly identified a supported cipher.
+*/
+QSslCipher::QSslCipher(const QString &name, QSsl::SslProtocol protocol)
+ : d(new QSslCipherPrivate)
+{
+ foreach (const QSslCipher &cipher, QSslSocket::supportedCiphers()) {
+ if (cipher.name() == name && cipher.protocol() == protocol) {
+ *this = cipher;
+ return;
+ }
+ }
+}
+
+/*!
+ Constructs an identical copy of the \a other cipher.
+*/
+QSslCipher::QSslCipher(const QSslCipher &other)
+ : d(new QSslCipherPrivate)
+{
+ *d.data() = *other.d.data();
+}
+
+/*!
+ Destroys the QSslCipher object.
+*/
+QSslCipher::~QSslCipher()
+{
+}
+
+/*!
+ Copies the contents of \a other into this cipher, making the two
+ ciphers identical.
+*/
+QSslCipher &QSslCipher::operator=(const QSslCipher &other)
+{
+ *d.data() = *other.d.data();
+ return *this;
+}
+
+/*!
+ Returns true if this cipher is the same as \a other; otherwise,
+ false is returned.
+*/
+bool QSslCipher::operator==(const QSslCipher &other) const
+{
+ return d->name == other.d->name && d->protocol == other.d->protocol;
+}
+
+/*!
+ \fn bool QSslCipher::operator!=(const QSslCipher &other) const
+
+ Returns true if this cipher is not the same as \a other;
+ otherwise, false is returned.
+*/
+
+/*!
+ Returns true if this is a null cipher; otherwise returns false.
+*/
+bool QSslCipher::isNull() const
+{
+ return d->isNull;
+}
+
+/*!
+ Returns the name of the cipher, or an empty QString if this is a null
+ cipher.
+
+ \sa isNull()
+*/
+QString QSslCipher::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the number of bits supported by the cipher.
+
+ \sa usedBits()
+*/
+int QSslCipher::supportedBits()const
+{
+ return d->supportedBits;
+}
+
+/*!
+ Returns the number of bits used by the cipher.
+
+ \sa supportedBits()
+*/
+int QSslCipher::usedBits() const
+{
+ return d->bits;
+}
+
+/*!
+ Returns the cipher's key exchange method as a QString.
+*/
+QString QSslCipher::keyExchangeMethod() const
+{
+ return d->keyExchangeMethod;
+}
+
+/*!
+ Returns the cipher's authentication method as a QString.
+*/
+QString QSslCipher::authenticationMethod() const
+{
+ return d->authenticationMethod;
+}
+
+/*!
+ Returns the cipher's encryption method as a QString.
+*/
+QString QSslCipher::encryptionMethod() const
+{
+ return d->encryptionMethod;
+}
+
+/*!
+ Returns the cipher's protocol as a QString.
+
+ \sa protocol()
+*/
+QString QSslCipher::protocolString() const
+{
+ return d->protocolString;
+}
+
+/*!
+ Returns the cipher's protocol type, or \l QSsl::UnknownProtocol if
+ QSslCipher is unable to determine the protocol (protocolString() may
+ contain more information).
+
+ \sa protocolString()
+*/
+QSsl::SslProtocol QSslCipher::protocol() const
+{
+ return d->protocol;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QSslCipher &cipher)
+{
+ debug << "QSslCipher(name=" << qPrintable(cipher.name())
+ << ", bits=" << cipher.usedBits()
+ << ", proto=" << qPrintable(cipher.protocolString())
+ << ')';
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h
new file mode 100644
index 0000000000..edaed2c2e8
--- /dev/null
+++ b/src/network/ssl/qsslcipher.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLCIPHER_H
+#define QSSLCIPHER_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qssl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+class QSslCipherPrivate;
+class Q_NETWORK_EXPORT QSslCipher
+{
+public:
+ QSslCipher();
+ QSslCipher(const QString &name, QSsl::SslProtocol protocol);
+ QSslCipher(const QSslCipher &other);
+ ~QSslCipher();
+ QSslCipher &operator=(const QSslCipher &other);
+ bool operator==(const QSslCipher &other) const;
+ inline bool operator!=(const QSslCipher &other) const { return !operator==(other); }
+
+ bool isNull() const;
+ QString name() const;
+ int supportedBits() const;
+ int usedBits() const;
+
+ QString keyExchangeMethod() const;
+ QString authenticationMethod() const;
+ QString encryptionMethod() const;
+ QString protocolString() const;
+ QSsl::SslProtocol protocol() const;
+
+private:
+ QScopedPointer<QSslCipherPrivate> d;
+ friend class QSslSocketBackendPrivate;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslCipher &cipher);
+#endif
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+
diff --git a/src/network/ssl/qsslcipher_p.h b/src/network/ssl/qsslcipher_p.h
new file mode 100644
index 0000000000..79fe911280
--- /dev/null
+++ b/src/network/ssl/qsslcipher_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsslcipher.h"
+
+QT_BEGIN_NAMESPACE
+
+// 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.
+//
+
+class QSslCipherPrivate
+{
+public:
+ QSslCipherPrivate()
+ : isNull(true), supportedBits(0), bits(0),
+ exportable(false), protocol(QSsl::UnknownProtocol)
+ {
+ }
+
+ bool isNull;
+ QString name;
+ int supportedBits;
+ int bits;
+ QString keyExchangeMethod;
+ QString authenticationMethod;
+ QString encryptionMethod;
+ bool exportable;
+ QString protocolString;
+ QSsl::SslProtocol protocol;
+};
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
new file mode 100644
index 0000000000..70d7dd8df1
--- /dev/null
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -0,0 +1,542 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsslconfiguration.h"
+#include "qsslconfiguration_p.h"
+#include "qsslsocket.h"
+#include "qmutex.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSslConfiguration
+ \brief The QSslConfiguration class holds the configuration and state of an SSL connection
+ \since 4.4
+
+ \reentrant
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup ssl
+
+ QSslConfiguration is used by Qt networking classes to relay
+ information about an open SSL connection and to allow the
+ application to control certain features of that connection.
+
+ The settings that QSslConfiguration currently supports are:
+
+ \list
+ \o The SSL/TLS protocol to be used
+ \o The certificate to be presented to the peer during connection
+ and its associated private key
+ \o The ciphers allowed to be used for encrypting the connection
+ \o The list of Certificate Authorities certificates that are
+ used to validate the peer's certificate
+ \endlist
+
+ These settings are applied only during the connection
+ handshake. Setting them after the connection has been established
+ has no effect.
+
+ The state that QSslConfiguration supports are:
+ \list
+ \o The certificate the peer presented during handshake, along
+ with the chain leading to a CA certificate
+ \o The cipher used to encrypt this session
+ \endlist
+
+ The state can only be obtained once the SSL connection starts, but
+ not necessarily before it's done. Some settings may change during
+ the course of the SSL connection without need to restart it (for
+ instance, the cipher can be changed over time).
+
+ State in QSslConfiguration objects cannot be changed.
+
+ QSslConfiguration can be used with QSslSocket and the Network
+ Access API.
+
+ Note that changing settings in QSslConfiguration is not enough to
+ change the settings in the related SSL connection. You must call
+ setSslConfiguration on a modified QSslConfiguration object to
+ achieve that. The following example illustrates how to change the
+ protocol to TLSv1 in a QSslSocket object:
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslconfiguration.cpp 0
+
+ \sa QSsl::SslProtocol, QSslCertificate, QSslCipher, QSslKey
+ QSslSocket, QNetworkAccessManager,
+ QSslSocket::sslConfiguration(), QSslSocket::setSslConfiguration()
+*/
+
+/*!
+ Constructs an empty SSL configuration. This configuration contains
+ no valid settings and the state will be empty. isNull() will
+ return true after this constructor is called.
+
+ Once any setter methods are called, isNull() will return false.
+*/
+QSslConfiguration::QSslConfiguration()
+ : d(new QSslConfigurationPrivate)
+{
+}
+
+/*!
+ Copies the configuration and state of \a other. If \a other is
+ null, this object will be null too.
+*/
+QSslConfiguration::QSslConfiguration(const QSslConfiguration &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Releases any resources held by QSslConfiguration.
+*/
+QSslConfiguration::~QSslConfiguration()
+{
+ // QSharedDataPointer deletes d for us if necessary
+}
+
+/*!
+ Copies the configuration and state of \a other. If \a other is
+ null, this object will be null too.
+*/
+QSslConfiguration &QSslConfiguration::operator=(const QSslConfiguration &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this QSslConfiguration object is equal to \a
+ other.
+
+ Two QSslConfiguration objects are considered equal if they have
+ the exact same settings and state.
+
+ \sa operator!=()
+*/
+bool QSslConfiguration::operator==(const QSslConfiguration &other) const
+{
+ if (d == other.d)
+ return true;
+ return d->peerCertificate == other.d->peerCertificate &&
+ d->peerCertificateChain == other.d->peerCertificateChain &&
+ d->localCertificate == other.d->localCertificate &&
+ d->privateKey == other.d->privateKey &&
+ d->sessionCipher == other.d->sessionCipher &&
+ d->ciphers == other.d->ciphers &&
+ d->caCertificates == other.d->caCertificates &&
+ d->protocol == other.d->protocol &&
+ d->peerVerifyMode == other.d->peerVerifyMode &&
+ d->peerVerifyDepth == other.d->peerVerifyDepth;
+}
+
+/*!
+ \fn QSslConfiguration::operator!=(const QSslConfiguration &other) const
+
+ Returns true if this QSslConfiguration differs from \a other. Two
+ QSslConfiguration objects are considered different if any state or
+ setting is different.
+
+ \sa operator==()
+*/
+
+/*!
+ Returns true if this is a null QSslConfiguration object.
+
+ A QSslConfiguration object is null if it has been
+ default-constructed and no setter methods have been called.
+
+ \sa setProtocol(), setLocalCertificate(), setPrivateKey(),
+ setCiphers(), setCaCertificates()
+*/
+bool QSslConfiguration::isNull() const
+{
+ 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);
+}
+
+/*!
+ Returns the protocol setting for this SSL configuration.
+
+ \sa setProtocol()
+*/
+QSsl::SslProtocol QSslConfiguration::protocol() const
+{
+ return d->protocol;
+}
+
+/*!
+ Sets the protocol setting for this configuration to be \a
+ protocol.
+
+ Setting the protocol once the connection has already been
+ established has no effect.
+
+ \sa protocol()
+*/
+void QSslConfiguration::setProtocol(QSsl::SslProtocol protocol)
+{
+ d->protocol = protocol;
+}
+
+/*!
+ Returns the verify mode. This mode decides whether QSslSocket should
+ request a certificate from the peer (i.e., the client requests a
+ certificate from the server, or a server requesting a certificate from the
+ 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.
+
+ \sa setPeerVerifyMode()
+*/
+QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const
+{
+ return d->peerVerifyMode;
+}
+
+/*!
+ Sets the verify mode to \a mode. This mode decides whether QSslSocket
+ should request a certificate from the peer (i.e., the client requests a
+ certificate from the server, or a server requesting a certificate from the
+ 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.
+
+ \sa peerVerifyMode()
+*/
+void QSslConfiguration::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode)
+{
+ d->peerVerifyMode = mode;
+}
+
+
+/*!
+ Returns the maximum number of certificates in the peer's certificate chain
+ to be checked during the SSL handshake phase, or 0 (the default) if no
+ maximum depth has been set, indicating that the whole certificate chain
+ should be checked.
+
+ The certificates are checked in issuing order, starting with the peer's
+ own certificate, then its issuer's certificate, and so on.
+
+ \sa setPeerVerifyDepth(), peerVerifyMode()
+*/
+int QSslConfiguration::peerVerifyDepth() const
+{
+ return d->peerVerifyDepth;
+}
+
+/*!
+ Sets the maximum number of certificates in the peer's certificate chain to
+ be checked during the SSL handshake phase, to \a depth. Setting a depth of
+ 0 means that no maximum depth is set, indicating that the whole
+ certificate chain should be checked.
+
+ The certificates are checked in issuing order, starting with the peer's
+ own certificate, then its issuer's certificate, and so on.
+
+ \sa peerVerifyDepth(), setPeerVerifyMode()
+*/
+void QSslConfiguration::setPeerVerifyDepth(int depth)
+{
+ if (depth < 0) {
+ qWarning("QSslConfiguration::setPeerVerifyDepth: cannot set negative depth of %d", depth);
+ return;
+ }
+ d->peerVerifyDepth = depth;
+}
+
+/*!
+ Returns the certificate to be presented to the peer during the SSL
+ handshake process.
+
+ \sa setLocalCertificate()
+*/
+QSslCertificate QSslConfiguration::localCertificate() const
+{
+ return d->localCertificate;
+}
+
+/*!
+ Sets the certificate to be presented to the peer during SSL
+ handshake to be \a certificate.
+
+ Setting the certificate once the connection has been established
+ has no effect.
+
+ A certificate is the means of identification used in the SSL
+ process. The local certificate is used by the remote end to verify
+ the local user's identity against its list of Certification
+ Authorities. In most cases, such as in HTTP web browsing, only
+ servers identify to the clients, so the client does not send a
+ certificate.
+
+ \sa localCertificate()
+*/
+void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate)
+{
+ d->localCertificate = certificate;
+}
+
+/*!
+ Returns the peer's digital certificate (i.e., the immediate
+ certificate of the host you are connected to), or a null
+ certificate, if the peer has not assigned a certificate.
+
+ The peer certificate is checked automatically during the
+ handshake phase, so this function is normally used to fetch
+ the certificate for display or for connection diagnostic
+ purposes. It contains information about the peer, including
+ its host name, the certificate issuer, and the peer's public
+ key.
+
+ Because the peer certificate is set during the handshake phase, it
+ is safe to access the peer certificate from a slot connected to
+ the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors()
+ signal, or the QSslSocket::encrypted() signal.
+
+ If a null certificate is returned, it can mean the SSL handshake
+ failed, or it can mean the host you are connected to doesn't have
+ a certificate, or it can mean there is no connection.
+
+ If you want to check the peer's complete chain of certificates,
+ use peerCertificateChain() to get them all at once.
+
+ \sa peerCertificateChain(),
+ QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(),
+ QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors()
+*/
+QSslCertificate QSslConfiguration::peerCertificate() const
+{
+ return d->peerCertificate;
+}
+
+/*!
+ Returns the peer's chain of digital certificates, starting with
+ the peer's immediate certificate and ending with the CA's
+ certificate.
+
+ Peer certificates are checked automatically during the handshake
+ phase. This function is normally used to fetch certificates for
+ display, or for performing connection diagnostics. Certificates
+ contain information about the peer and the certificate issuers,
+ including host name, issuer names, and issuer public keys.
+
+ Because the peer certificate is set during the handshake phase, it
+ is safe to access the peer certificate from a slot connected to
+ the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors()
+ signal, or the QSslSocket::encrypted() signal.
+
+ If an empty list is returned, it can mean the SSL handshake
+ failed, or it can mean the host you are connected to doesn't have
+ a certificate, or it can mean there is no connection.
+
+ If you want to get only the peer's immediate certificate, use
+ peerCertificate().
+
+ \sa peerCertificate(),
+ QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(),
+ QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors()
+*/
+QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const
+{
+ return d->peerCertificateChain;
+}
+
+/*!
+ Returns the socket's cryptographic \l {QSslCipher} {cipher}, or a
+ null cipher if the connection isn't encrypted. The socket's cipher
+ for the session is set during the handshake phase. The cipher is
+ used to encrypt and decrypt data transmitted through the socket.
+
+ The SSL infrastructure also provides functions for setting the
+ ordered list of ciphers from which the handshake phase will
+ eventually select the session cipher. This ordered list must be in
+ place before the handshake phase begins.
+
+ \sa ciphers(), setCiphers(), QSslSocket::supportedCiphers()
+*/
+QSslCipher QSslConfiguration::sessionCipher() const
+{
+ return d->sessionCipher;
+}
+
+/*!
+ Returns the \l {QSslKey} {SSL key} assigned to this connection or
+ a null key if none has been assigned yet.
+
+ \sa setPrivateKey(), localCertificate()
+*/
+QSslKey QSslConfiguration::privateKey() const
+{
+ return d->privateKey;
+}
+
+/*!
+ Sets the connection's private \l {QSslKey} {key} to \a key. The
+ private key and the local \l {QSslCertificate} {certificate} are
+ used by clients and servers that must prove their identity to
+ SSL peers.
+
+ Both the key and the local certificate are required if you are
+ creating an SSL server socket. If you are creating an SSL client
+ socket, the key and local certificate are required if your client
+ must identify itself to an SSL server.
+
+ \sa privateKey(), setLocalCertificate()
+*/
+void QSslConfiguration::setPrivateKey(const QSslKey &key)
+{
+ d->privateKey = key;
+}
+
+/*!
+ Returns this connection's current cryptographic cipher suite. This
+ list is used during the handshake phase for choosing a
+ session cipher. The returned list of ciphers is ordered by
+ descending preference. (i.e., the first cipher in the list is the
+ most preferred cipher). The session cipher will be the first one
+ in the list that is also supported by the peer.
+
+ By default, the handshake phase can choose any of the ciphers
+ supported by this system's SSL libraries, which may vary from
+ system to system. The list of ciphers supported by this system's
+ SSL libraries is returned by QSslSocket::supportedCiphers(). You can restrict
+ the list of ciphers used for choosing the session cipher for this
+ socket by calling setCiphers() with a subset of the supported
+ ciphers. You can revert to using the entire set by calling
+ setCiphers() with the list returned by QSslSocket::supportedCiphers().
+
+ \sa setCiphers(), QSslSocket::supportedCiphers()
+*/
+QList<QSslCipher> QSslConfiguration::ciphers() const
+{
+ return d->ciphers;
+}
+
+/*!
+ Sets the cryptographic cipher suite for this socket to \a ciphers,
+ which must contain a subset of the ciphers in the list returned by
+ supportedCiphers().
+
+ Restricting the cipher suite must be done before the handshake
+ phase, where the session cipher is chosen.
+
+ \sa ciphers(), QSslSocket::supportedCiphers()
+*/
+void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
+{
+ d->ciphers = ciphers;
+}
+
+/*!
+ Returns this connection's CA certificate database. The CA certificate
+ database is used by the socket during the handshake phase to
+ validate the peer's certificate. It can be modified prior to the
+ handshake with setCaCertificates(), or with \l{QSslSocket}'s
+ \l{QSslSocket::}{addCaCertificate()} and
+ \l{QSslSocket::}{addCaCertificates()}.
+
+ \sa setCaCertificates()
+*/
+QList<QSslCertificate> QSslConfiguration::caCertificates() const
+{
+ return d->caCertificates;
+}
+
+/*!
+ Sets this socket's CA certificate database to be \a certificates.
+ The certificate database must be set prior to the SSL handshake.
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ \sa caCertificates()
+*/
+void QSslConfiguration::setCaCertificates(const QList<QSslCertificate> &certificates)
+{
+ d->caCertificates = certificates;
+}
+
+/*!
+ Returns the default SSL configuration to be used in new SSL
+ connections.
+
+ The default SSL configuration consists of:
+
+ \list
+ \o no local certificate and no private key
+ \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
+ \endlist
+
+ \sa QSslSocket::supportedCiphers(), setDefaultConfiguration()
+*/
+QSslConfiguration QSslConfiguration::defaultConfiguration()
+{
+ return QSslConfigurationPrivate::defaultConfiguration();
+}
+
+/*!
+ Sets the default SSL configuration to be used in new SSL
+ connections to be \a configuration. Existing connections are not
+ affected by this call.
+
+ \sa QSslSocket::supportedCiphers(), defaultConfiguration()
+*/
+void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configuration)
+{
+ QSslConfigurationPrivate::setDefaultConfiguration(configuration);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
new file mode 100644
index 0000000000..143566bef1
--- /dev/null
+++ b/src/network/ssl/qsslconfiguration.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** In addition, as a special exception, Nokia gives permission to link
+** the code of its release of Qt with the OpenSSL project's "OpenSSL" library
+** (or modified versions of the "OpenSSL" library that use the same license
+** as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#ifndef QSSLCONFIGURATION_H
+#define QSSLCONFIGURATION_H
+
+#include <QtCore/qshareddata.h>
+#include <QtNetwork/qsslsocket.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+template<typename T> class QList;
+class QSslCertificate;
+class QSslCipher;
+class QSslKey;
+
+class QSslConfigurationPrivate;
+class Q_NETWORK_EXPORT QSslConfiguration
+{
+public:
+ QSslConfiguration();
+ QSslConfiguration(const QSslConfiguration &other);
+ ~QSslConfiguration();
+ QSslConfiguration &operator=(const QSslConfiguration &other);
+
+ bool operator==(const QSslConfiguration &other) const;
+ inline bool operator!=(const QSslConfiguration &other) const
+ { return !(*this == other); }
+
+ bool isNull() const; // ### Qt 5: remove; who would need this?
+
+ QSsl::SslProtocol protocol() const;
+ void setProtocol(QSsl::SslProtocol protocol);
+
+ // Verification
+ QSslSocket::PeerVerifyMode peerVerifyMode() const;
+ void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode);
+
+ int peerVerifyDepth() const;
+ void setPeerVerifyDepth(int depth);
+
+ // Certificate & cipher configuration
+ QSslCertificate localCertificate() const;
+ void setLocalCertificate(const QSslCertificate &certificate);
+
+ QSslCertificate peerCertificate() const;
+ QList<QSslCertificate> peerCertificateChain() const;
+ QSslCipher sessionCipher() const;
+
+ // Private keys, for server sockets
+ QSslKey privateKey() const;
+ void setPrivateKey(const QSslKey &key);
+
+ // Cipher settings
+ QList<QSslCipher> ciphers() const;
+ void setCiphers(const QList<QSslCipher> &ciphers);
+
+ // Certificate Authority (CA) settings
+ QList<QSslCertificate> caCertificates() const;
+ void setCaCertificates(const QList<QSslCertificate> &certificates);
+
+ static QSslConfiguration defaultConfiguration();
+ static void setDefaultConfiguration(const QSslConfiguration &configuration);
+
+private:
+ friend class QSslSocket;
+ friend class QSslConfigurationPrivate;
+ QSslConfiguration(QSslConfigurationPrivate *dd);
+ QSharedDataPointer<QSslConfigurationPrivate> d;
+};
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
new file mode 100644
index 0000000000..a5af51a8db
--- /dev/null
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/****************************************************************************
+**
+** In addition, as a special exception, Nokia gives permission to link
+** the code of its release of Qt with the OpenSSL project's "OpenSSL" library
+** (or modified versions of the "OpenSSL" library that use the same license
+** as the original version), and distribute the linked executables.
+**
+** You must comply with the GNU General Public License version 2 in all
+** respects for all of the code used other than the "OpenSSL" code. If you
+** modify this file, you may extend this exception to your version of the file,
+** but you are not obligated to do so. If you do not wish to do so, delete
+** this exception statement from your version of this file.
+**
+****************************************************************************/
+
+#ifndef QSSLCONFIGURATION_P_H
+#define QSSLCONFIGURATION_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 QSslSocket API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsslconfiguration.h"
+#include "qlist.h"
+#include "qsslcertificate.h"
+#include "qsslcipher.h"
+#include "qsslkey.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSslConfigurationPrivate: public QSharedData
+{
+public:
+ QSslConfigurationPrivate()
+ : protocol(QSsl::SecureProtocols),
+ peerVerifyMode(QSslSocket::AutoVerifyPeer),
+ peerVerifyDepth(0)
+ { }
+
+ QSslCertificate peerCertificate;
+ QList<QSslCertificate> peerCertificateChain;
+ QSslCertificate localCertificate;
+
+ QSslKey privateKey;
+ QSslCipher sessionCipher;
+ QList<QSslCipher> ciphers;
+ QList<QSslCertificate> caCertificates;
+
+ QSsl::SslProtocol protocol;
+ QSslSocket::PeerVerifyMode peerVerifyMode;
+ int peerVerifyDepth;
+
+ // in qsslsocket.cpp:
+ static QSslConfiguration defaultConfiguration();
+ static void setDefaultConfiguration(const QSslConfiguration &configuration);
+ static void deepCopyDefaultConfiguration(QSslConfigurationPrivate *config);
+};
+
+// implemented here for inlining purposes
+inline QSslConfiguration::QSslConfiguration(QSslConfigurationPrivate *dd)
+ : d(dd)
+{
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
new file mode 100644
index 0000000000..ae18b47170
--- /dev/null
+++ b/src/network/ssl/qsslerror.cpp
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QSslError
+ \brief The QSslError class provides an SSL error.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ QSslError provides a simple API for managing errors during QSslSocket's
+ SSL handshake.
+
+ \sa QSslSocket, QSslCertificate, QSslCipher
+*/
+
+/*!
+ \enum QSslError::SslError
+
+ Describes all recognized errors that can occur during an SSL handshake.
+
+ \value NoError
+ \value UnableToGetIssuerCertificate
+ \value UnableToDecryptCertificateSignature
+ \value UnableToDecodeIssuerPublicKey
+ \value CertificateSignatureFailed
+ \value CertificateNotYetValid
+ \value CertificateExpired
+ \value InvalidNotBeforeField
+ \value InvalidNotAfterField
+ \value SelfSignedCertificate
+ \value SelfSignedCertificateInChain
+ \value UnableToGetLocalIssuerCertificate
+ \value UnableToVerifyFirstCertificate
+ \value CertificateRevoked
+ \value InvalidCaCertificate
+ \value PathLengthExceeded
+ \value InvalidPurpose
+ \value CertificateUntrusted
+ \value CertificateRejected
+ \value SubjectIssuerMismatch
+ \value AuthorityIssuerSerialNumberMismatch
+ \value NoPeerCertificate
+ \value HostNameMismatch
+ \value UnspecifiedError
+ \value NoSslSupport
+ \value CertificateBlacklisted
+
+ \sa QSslError::errorString()
+*/
+
+#include "qsslerror.h"
+#include "qsslsocket.h"
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+#endif
+
+class QSslErrorPrivate
+{
+public:
+ QSslError::SslError error;
+ QSslCertificate certificate;
+};
+
+/*!
+ Constructs a QSslError object with no error and default certificate.
+
+*/
+
+// RVCT compiler in debug build does not like about default values in const-
+// So as an workaround we define all constructor overloads here explicitly
+QSslError::QSslError()
+ : d(new QSslErrorPrivate)
+{
+ d->error = QSslError::NoError;
+ d->certificate = QSslCertificate();
+}
+
+/*!
+ Constructs a QSslError object. The argument specifies the \a
+ error that occurred.
+
+*/
+QSslError::QSslError(SslError error)
+ : d(new QSslErrorPrivate)
+{
+ d->error = error;
+ d->certificate = QSslCertificate();
+}
+
+/*!
+ Constructs a QSslError object. The two arguments specify the \a
+ error that occurred, and which \a certificate the error relates to.
+
+ \sa QSslCertificate
+*/
+QSslError::QSslError(SslError error, const QSslCertificate &certificate)
+ : d(new QSslErrorPrivate)
+{
+ d->error = error;
+ d->certificate = certificate;
+}
+
+/*!
+ Constructs an identical copy of \a other.
+*/
+QSslError::QSslError(const QSslError &other)
+ : d(new QSslErrorPrivate)
+{
+ *d.data() = *other.d.data();
+}
+
+/*!
+ Destroys the QSslError object.
+*/
+QSslError::~QSslError()
+{
+}
+
+/*!
+ \since 4.4
+
+ Assigns the contents of \a other to this error.
+*/
+QSslError &QSslError::operator=(const QSslError &other)
+{
+ *d.data() = *other.d.data();
+ return *this;
+}
+
+/*!
+ \since 4.4
+
+ Returns true if this error is equal to \a other; otherwise returns false.
+*/
+bool QSslError::operator==(const QSslError &other) const
+{
+ return d->error == other.d->error
+ && d->certificate == other.d->certificate;
+}
+
+/*!
+ \fn bool QSslError::operator!=(const QSslError &other) const
+ \since 4.4
+
+ Returns true if this error is not equal to \a other; otherwise returns
+ false.
+*/
+
+/*!
+ Returns the type of the error.
+
+ \sa errorString(), certificate()
+*/
+QSslError::SslError QSslError::error() const
+{
+ return d->error;
+}
+
+/*!
+ Returns a short localized human-readable description of the error.
+
+ \sa error(), certificate()
+*/
+QString QSslError::errorString() const
+{
+ QString errStr;
+ switch (d->error) {
+ case NoError:
+ errStr = QSslSocket::tr("No error");
+ break;
+ case UnableToGetIssuerCertificate:
+ errStr = QSslSocket::tr("The issuer certificate could not be found");
+ break;
+ case UnableToDecryptCertificateSignature:
+ errStr = QSslSocket::tr("The certificate signature could not be decrypted");
+ break;
+ case UnableToDecodeIssuerPublicKey:
+ errStr = QSslSocket::tr("The public key in the certificate could not be read");
+ break;
+ case CertificateSignatureFailed:
+ errStr = QSslSocket::tr("The signature of the certificate is invalid");
+ break;
+ case CertificateNotYetValid:
+ errStr = QSslSocket::tr("The certificate is not yet valid");
+ break;
+ case CertificateExpired:
+ errStr = QSslSocket::tr("The certificate has expired");
+ break;
+ case InvalidNotBeforeField:
+ errStr = QSslSocket::tr("The certificate's notBefore field contains an invalid time");
+ break;
+ case InvalidNotAfterField:
+ errStr = QSslSocket::tr("The certificate's notAfter field contains an invalid time");
+ break;
+ case SelfSignedCertificate:
+ errStr = QSslSocket::tr("The certificate is self-signed, and untrusted");
+ break;
+ case SelfSignedCertificateInChain:
+ errStr = QSslSocket::tr("The root certificate of the certificate chain is self-signed, and untrusted");
+ break;
+ case UnableToGetLocalIssuerCertificate:
+ errStr = QSslSocket::tr("The issuer certificate of a locally looked up certificate could not be found");
+ break;
+ case UnableToVerifyFirstCertificate:
+ errStr = QSslSocket::tr("No certificates could be verified");
+ break;
+ case InvalidCaCertificate:
+ errStr = QSslSocket::tr("One of the CA certificates is invalid");
+ break;
+ case PathLengthExceeded:
+ errStr = QSslSocket::tr("The basicConstraints path length parameter has been exceeded");
+ break;
+ case InvalidPurpose:
+ errStr = QSslSocket::tr("The supplied certificate is unsuitable for this purpose");
+ break;
+ case CertificateUntrusted:
+ errStr = QSslSocket::tr("The root CA certificate is not trusted for this purpose");
+ break;
+ case CertificateRejected:
+ errStr = QSslSocket::tr("The root CA certificate is marked to reject the specified purpose");
+ break;
+ case SubjectIssuerMismatch: // hostname mismatch
+ errStr = QSslSocket::tr("The current candidate issuer certificate was rejected because its"
+ " subject name did not match the issuer name of the current certificate");
+ break;
+ case AuthorityIssuerSerialNumberMismatch:
+ errStr = QSslSocket::tr("The current candidate issuer certificate was rejected because"
+ " its issuer name and serial number was present and did not match the"
+ " authority key identifier of the current certificate");
+ break;
+ case NoPeerCertificate:
+ errStr = QSslSocket::tr("The peer did not present any certificate");
+ break;
+ case HostNameMismatch:
+ errStr = QSslSocket::tr("The host name did not match any of the valid hosts"
+ " for this certificate");
+ break;
+ case NoSslSupport:
+ break;
+ case CertificateBlacklisted:
+ errStr = QSslSocket::tr("The peer certificate is blacklisted");
+ break;
+ default:
+ errStr = QSslSocket::tr("Unknown error");
+ break;
+ }
+
+ return errStr;
+}
+
+/*!
+ Returns the certificate associated with this error, or a null certificate
+ if the error does not relate to any certificate.
+
+ \sa error(), errorString()
+*/
+QSslCertificate QSslError::certificate() const
+{
+ return d->certificate;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+//class QDebug;
+QDebug operator<<(QDebug debug, const QSslError &error)
+{
+ debug << error.errorString();
+ return debug;
+}
+QDebug operator<<(QDebug debug, const QSslError::SslError &error)
+{
+ debug << QSslError(error).errorString();
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
new file mode 100644
index 0000000000..c30c02a8af
--- /dev/null
+++ b/src/network/ssl/qsslerror.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLERROR_H
+#define QSSLERROR_H
+
+#include <QtCore/qvariant.h>
+#include <QtNetwork/qsslcertificate.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+class QSslErrorPrivate;
+class Q_NETWORK_EXPORT QSslError
+{
+public:
+ enum SslError {
+ NoError,
+ UnableToGetIssuerCertificate,
+ UnableToDecryptCertificateSignature,
+ UnableToDecodeIssuerPublicKey,
+ CertificateSignatureFailed,
+ CertificateNotYetValid,
+ CertificateExpired,
+ InvalidNotBeforeField,
+ InvalidNotAfterField,
+ SelfSignedCertificate,
+ SelfSignedCertificateInChain,
+ UnableToGetLocalIssuerCertificate,
+ UnableToVerifyFirstCertificate,
+ CertificateRevoked,
+ InvalidCaCertificate,
+ PathLengthExceeded,
+ InvalidPurpose,
+ CertificateUntrusted,
+ CertificateRejected,
+ SubjectIssuerMismatch, // hostname mismatch?
+ AuthorityIssuerSerialNumberMismatch,
+ NoPeerCertificate,
+ HostNameMismatch,
+ NoSslSupport,
+ CertificateBlacklisted,
+ UnspecifiedError = -1
+ };
+
+ // RVCT compiler in debug build does not like about default values in const-
+ // So as an workaround we define all constructor overloads here explicitly
+ QSslError();
+ QSslError(SslError error);
+ QSslError(SslError error, const QSslCertificate &certificate);
+
+ QSslError(const QSslError &other);
+
+ ~QSslError();
+ QSslError &operator=(const QSslError &other);
+ bool operator==(const QSslError &other) const;
+ inline bool operator!=(const QSslError &other) const
+ { return !(*this == other); }
+
+ SslError error() const;
+ QString errorString() const;
+ QSslCertificate certificate() const;
+
+private:
+ QScopedPointer<QSslErrorPrivate> d;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError &error);
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslError::SslError &error);
+#endif
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/ssl/qsslkey.cpp b/src/network/ssl/qsslkey.cpp
new file mode 100644
index 0000000000..8b32f65405
--- /dev/null
+++ b/src/network/ssl/qsslkey.cpp
@@ -0,0 +1,460 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QSslKey
+ \brief The QSslKey class provides an interface for private and public keys.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ QSslKey provides a simple API for managing keys.
+
+ \sa QSslSocket, QSslCertificate, QSslCipher
+*/
+
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qsslkey.h"
+#include "qsslkey_p.h"
+#include "qsslsocket.h"
+#include "qsslsocket_p.h"
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qiodevice.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+#endif
+
+
+/*!
+ \internal
+ */
+void QSslKeyPrivate::clear(bool deep)
+{
+ isNull = true;
+ if (!QSslSocket::supportsSsl())
+ return;
+ if (rsa) {
+ if (deep)
+ q_RSA_free(rsa);
+ rsa = 0;
+ }
+ if (dsa) {
+ if (deep)
+ q_DSA_free(dsa);
+ dsa = 0;
+ }
+}
+
+/*!
+ \internal
+
+ Allocates a new rsa or dsa struct and decodes \a pem into it
+ according to the current algorithm and type.
+
+ If \a deepClear is true, the rsa/dsa struct is freed if it is was
+ already allocated, otherwise we "leak" memory (which is exactly
+ what we want for copy construction).
+
+ If \a passPhrase is non-empty, it will be used for decrypting
+ \a pem.
+*/
+void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
+ bool deepClear)
+{
+ if (pem.isEmpty())
+ return;
+
+ clear(deepClear);
+
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
+ if (!bio)
+ return;
+
+ void *phrase = (void *)passPhrase.constData();
+
+ if (algorithm == QSsl::Rsa) {
+ RSA *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, phrase)
+ : q_PEM_read_bio_RSAPrivateKey(bio, &rsa, 0, phrase);
+ if (rsa && rsa == result)
+ isNull = false;
+ } else {
+ DSA *result = (type == QSsl::PublicKey)
+ ? q_PEM_read_bio_DSA_PUBKEY(bio, &dsa, 0, phrase)
+ : q_PEM_read_bio_DSAPrivateKey(bio, &dsa, 0, phrase);
+ if (dsa && dsa == result)
+ isNull = false;
+ }
+
+ q_BIO_free(bio);
+}
+
+/*!
+ Constructs a null key.
+
+ \sa isNull()
+*/
+QSslKey::QSslKey()
+ : d(new QSslKeyPrivate)
+{
+}
+
+/*!
+ \internal
+*/
+QByteArray QSslKeyPrivate::pemHeader() const
+{
+ // ### use QByteArray::fromRawData() instead
+ if (type == QSsl::PublicKey)
+ return QByteArray("-----BEGIN PUBLIC KEY-----\n");
+ else if (algorithm == QSsl::Rsa)
+ return QByteArray("-----BEGIN RSA PRIVATE KEY-----\n");
+ return QByteArray("-----BEGIN DSA PRIVATE KEY-----\n");
+}
+
+/*!
+ \internal
+*/
+QByteArray QSslKeyPrivate::pemFooter() const
+{
+ // ### use QByteArray::fromRawData() instead
+ if (type == QSsl::PublicKey)
+ return QByteArray("-----END PUBLIC KEY-----\n");
+ else if (algorithm == QSsl::Rsa)
+ return QByteArray("-----END RSA PRIVATE KEY-----\n");
+ return QByteArray("-----END DSA PRIVATE KEY-----\n");
+}
+
+/*!
+ \internal
+
+ Returns a DER key formatted as PEM.
+*/
+QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
+{
+ QByteArray pem(der.toBase64());
+
+ const int lineWidth = 64; // RFC 1421
+ const int newLines = pem.size() / lineWidth;
+ const bool rem = pem.size() % lineWidth;
+
+ // ### optimize
+ for (int i = 0; i < newLines; ++i)
+ pem.insert((i + 1) * lineWidth + i, '\n');
+ if (rem)
+ pem.append('\n'); // ###
+
+ pem.prepend(pemHeader());
+ pem.append(pemFooter());
+
+ return pem;
+}
+
+/*!
+ \internal
+
+ Returns a PEM key formatted as DER.
+*/
+QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
+{
+ const QByteArray header = pemHeader();
+ const QByteArray footer = pemFooter();
+
+ QByteArray der(pem);
+
+ const int headerIndex = der.indexOf(header);
+ const int footerIndex = der.indexOf(footer);
+ if (headerIndex == -1 || footerIndex == -1)
+ return QByteArray();
+
+ der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
+
+ return QByteArray::fromBase64(der); // ignores newlines
+}
+
+/*!
+ Constructs a QSslKey by decoding the string in the byte array
+ \a encoded using a specified \a algorithm and \a encoding format.
+ If the encoded key is encrypted, \a passPhrase is used to decrypt
+ it. \a type specifies whether the key is public or private.
+
+ After construction, use isNull() to check if \a encoded contained
+ a valid key.
+*/
+QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
+ QSsl::EncodingFormat encoding, QSsl::KeyType type, const QByteArray &passPhrase)
+ : d(new QSslKeyPrivate)
+{
+ d->type = type;
+ d->algorithm = algorithm;
+ d->decodePem((encoding == QSsl::Der)
+ ? d->pemFromDer(encoded) : encoded,
+ passPhrase);
+}
+
+/*!
+ Constructs a QSslKey by reading and decoding data from a
+ \a device using a specified \a algorithm and \a encoding format.
+ If the encoded key is encrypted, \a passPhrase is used to decrypt
+ it. \a type specifies whether the key is public or private.
+
+ After construction, use isNull() to check if \a device provided
+ a valid key.
+*/
+QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::EncodingFormat encoding,
+ QSsl::KeyType type, const QByteArray &passPhrase)
+ : d(new QSslKeyPrivate)
+{
+ QByteArray encoded;
+ if (device)
+ encoded = device->readAll();
+ d->type = type;
+ d->algorithm = algorithm;
+ d->decodePem((encoding == QSsl::Der) ?
+ d->pemFromDer(encoded) : encoded,
+ passPhrase);
+}
+
+/*!
+ Constructs an identical copy of \a other.
+*/
+QSslKey::QSslKey(const QSslKey &other) : d(other.d)
+{
+}
+
+/*!
+ Destroys the QSslKey object.
+*/
+QSslKey::~QSslKey()
+{
+}
+
+/*!
+ Copies the contents of \a other into this key, making the two keys
+ identical.
+
+ Returns a reference to this QSslKey.
+*/
+QSslKey &QSslKey::operator=(const QSslKey &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Returns true if this is a null key; otherwise false.
+
+ \sa clear()
+*/
+bool QSslKey::isNull() const
+{
+ return d->isNull;
+}
+
+/*!
+ Clears the contents of this key, making it a null key.
+
+ \sa isNull()
+*/
+void QSslKey::clear()
+{
+ d = new QSslKeyPrivate;
+}
+
+/*!
+ Returns the length of the key in bits, or -1 if the key is null.
+*/
+int QSslKey::length() const
+{
+ if (d->isNull)
+ return -1;
+ return (d->algorithm == QSsl::Rsa)
+ ? q_BN_num_bits(d->rsa->n) : q_BN_num_bits(d->dsa->p);
+}
+
+/*!
+ Returns the type of the key (i.e., PublicKey or PrivateKey).
+*/
+QSsl::KeyType QSslKey::type() const
+{
+ return d->type;
+}
+
+/*!
+ Returns the key algorithm.
+*/
+QSsl::KeyAlgorithm QSslKey::algorithm() const
+{
+ return d->algorithm;
+}
+
+/*!
+ Returns the key in DER encoding. The result is encrypted with
+ \a passPhrase if the key is a private key and \a passPhrase is
+ non-empty.
+*/
+// ### autotest failure for non-empty passPhrase and private key
+QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
+{
+ if (d->isNull)
+ return QByteArray();
+ return d->derFromPem(toPem(passPhrase));
+}
+
+/*!
+ Returns the key in PEM encoding. The result is encrypted with
+ \a passPhrase if the key is a private key and \a passPhrase is
+ non-empty.
+*/
+QByteArray QSslKey::toPem(const QByteArray &passPhrase) const
+{
+ if (!QSslSocket::supportsSsl() || d->isNull)
+ return QByteArray();
+
+ BIO *bio = q_BIO_new(q_BIO_s_mem());
+ if (!bio)
+ return QByteArray();
+
+ bool fail = false;
+
+ if (d->algorithm == QSsl::Rsa) {
+ if (d->type == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_RSA_PUBKEY(bio, d->rsa))
+ fail = true;
+ } else {
+ if (!q_PEM_write_bio_RSAPrivateKey(
+ bio, d->rsa,
+ // ### the cipher should be selectable in the API:
+ passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
+ (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
+ fail = true;
+ }
+ }
+ } else {
+ if (d->type == QSsl::PublicKey) {
+ if (!q_PEM_write_bio_DSA_PUBKEY(bio, d->dsa))
+ fail = true;
+ } else {
+ if (!q_PEM_write_bio_DSAPrivateKey(
+ bio, d->dsa,
+ // ### the cipher should be selectable in the API:
+ passPhrase.isEmpty() ? (const EVP_CIPHER *)0 : q_EVP_des_ede3_cbc(),
+ (uchar *)passPhrase.data(), passPhrase.size(), 0, 0)) {
+ fail = true;
+ }
+ }
+ }
+
+ QByteArray pem;
+ if (!fail) {
+ char *data;
+ long size = q_BIO_get_mem_data(bio, &data);
+ pem = QByteArray(data, size);
+ }
+ q_BIO_free(bio);
+ return pem;
+}
+
+/*!
+ Returns a pointer to the native key handle, if it is available;
+ otherwise a null pointer is returned.
+
+ You can use this handle together with the native API to access
+ extended information about the key.
+
+ \warning Use of this function has a high probability of being
+ non-portable, and its return value may vary across platforms, and
+ between minor Qt releases.
+*/
+Qt::HANDLE QSslKey::handle() const
+{
+ return (d->algorithm == QSsl::Rsa) ? Qt::HANDLE(d->rsa) : Qt::HANDLE(d->dsa);
+}
+
+/*!
+ Returns true if this key is equal to \a other; otherwise returns false.
+*/
+bool QSslKey::operator==(const QSslKey &other) const
+{
+ if (isNull())
+ return other.isNull();
+ if (other.isNull())
+ return isNull();
+ if (algorithm() != other.algorithm())
+ return false;
+ if (type() != other.type())
+ return false;
+ if (length() != other.length())
+ return false;
+ return toDer() == other.toDer();
+}
+
+/*! \fn bool QSslKey::operator!=(const QSslKey &other) const
+
+ Returns true if this key is not equal to key \a other; otherwise
+ returns false.
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+QDebug operator<<(QDebug debug, const QSslKey &key)
+{
+ debug << "QSslKey("
+ << (key.type() == QSsl::PublicKey ? "PublicKey" : "PrivateKey")
+ << ", " << (key.algorithm() == QSsl::Rsa ? "RSA" : "DSA")
+ << ", " << key.length()
+ << ')';
+ return debug;
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
new file mode 100644
index 0000000000..89973042f9
--- /dev/null
+++ b/src/network/ssl/qsslkey.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLKEY_H
+#define QSSLKEY_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtNetwork/qssl.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+template <typename A, typename B> struct QPair;
+
+class QIODevice;
+
+class QSslKeyPrivate;
+class Q_NETWORK_EXPORT QSslKey
+{
+public:
+ QSslKey();
+ QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm,
+ QSsl::EncodingFormat format = QSsl::Pem,
+ QSsl::KeyType type = QSsl::PrivateKey,
+ const QByteArray &passPhrase = QByteArray());
+ QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm,
+ QSsl::EncodingFormat format = QSsl::Pem,
+ QSsl::KeyType type = QSsl::PrivateKey,
+ const QByteArray &passPhrase = QByteArray());
+ QSslKey(const QSslKey &other);
+ ~QSslKey();
+ QSslKey &operator=(const QSslKey &other);
+
+ bool isNull() const;
+ void clear();
+
+ int length() const;
+ QSsl::KeyType type() const;
+ QSsl::KeyAlgorithm algorithm() const;
+
+ QByteArray toPem(const QByteArray &passPhrase = QByteArray()) const;
+ QByteArray toDer(const QByteArray &passPhrase = QByteArray()) const;
+
+ Qt::HANDLE handle() const;
+
+ bool operator==(const QSslKey &key) const;
+ inline bool operator!=(const QSslKey &key) const { return !operator==(key); }
+
+private:
+ QExplicitlySharedDataPointer<QSslKeyPrivate> d;
+ friend class QSslCertificate;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslKey &key);
+#endif
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
new file mode 100644
index 0000000000..e476ecea8c
--- /dev/null
+++ b/src/network/ssl/qsslkey_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLKEY_P_H
+#define QSSLKEY_P_H
+
+#include "qsslkey.h"
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qsslcertificate.cpp. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSslKeyPrivate
+{
+public:
+ inline QSslKeyPrivate()
+ : rsa(0)
+ , dsa(0)
+ {
+ clear();
+ }
+
+ inline ~QSslKeyPrivate()
+ { clear(); }
+
+ void clear(bool deep = true);
+
+ void decodePem(const QByteArray &pem, const QByteArray &passPhrase,
+ bool deepClear = true);
+ QByteArray pemHeader() const;
+ QByteArray pemFooter() const;
+ QByteArray pemFromDer(const QByteArray &der) const;
+ QByteArray derFromPem(const QByteArray &pem) const;
+
+ bool isNull;
+ QSsl::KeyType type;
+ QSsl::KeyAlgorithm algorithm;
+ RSA *rsa;
+ DSA *dsa;
+
+ QAtomicInt ref;
+
+private:
+ Q_DISABLE_COPY(QSslKeyPrivate)
+};
+
+QT_END_NAMESPACE
+
+#endif // QSSLKEY_P_H
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
new file mode 100644
index 0000000000..0dbf4b5196
--- /dev/null
+++ b/src/network/ssl/qsslsocket.cpp
@@ -0,0 +1,2260 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+//#define QSSLSOCKET_DEBUG
+
+/*!
+ \class QSslSocket
+ \brief The QSslSocket class provides an SSL encrypted socket for both
+ clients and servers.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \ingroup ssl
+ \inmodule QtNetwork
+
+ 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 TLSv1, but you can
+ change the SSL protocol by calling setProtocol() as long as you do
+ it before the handshake has started.
+
+ SSL encryption operates on top of the existing TCP stream after
+ the socket enters the ConnectedState. There are two simple ways to
+ establish a secure connection using QSslSocket: With an immediate
+ SSL handshake, or with a delayed SSL handshake occurring after the
+ connection has been established in unencrypted mode.
+
+ The most common way to use QSslSocket is to construct an object
+ and start a secure connection by calling connectToHostEncrypted().
+ This method starts an immediate SSL handshake once the connection
+ has been established.
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 0
+
+ As with a plain QTcpSocket, QSslSocket enters the HostLookupState,
+ ConnectingState, and finally the ConnectedState, if the connection
+ is successful. The handshake then starts automatically, and if it
+ succeeds, the encrypted() signal is emitted to indicate the socket
+ has entered the encrypted state and is ready for use.
+
+ Note that data can be written to the socket immediately after the
+ return from connectToHostEncrypted() (i.e., before the encrypted()
+ signal is emitted). The data is queued in QSslSocket until after
+ the encrypted() signal is emitted.
+
+ An example of using the delayed SSL handshake to secure an
+ existing connection is the case where an SSL server secures an
+ incoming connection. Suppose you create an SSL server class as a
+ subclass of QTcpServer. You would override
+ QTcpServer::incomingConnection() with something like the example
+ below, which first constructs an instance of QSslSocket and then
+ calls setSocketDescriptor() to set the new socket's descriptor to
+ the existing one passed in. It then initiates the SSL handshake
+ by calling startServerEncryption().
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 1
+
+ If an error occurs, QSslSocket emits the sslErrors() signal. In this
+ case, if no action is taken to ignore the error(s), the connection
+ is dropped. To continue, despite the occurrence of an error, you
+ can call ignoreSslErrors(), either from within this slot after the
+ error occurs, or any time after construction of the QSslSocket and
+ before the connection is attempted. This will allow QSslSocket to
+ ignore the errors it encounters when establishing the identity of
+ the peer. Ignoring errors during an SSL handshake should be used
+ with caution, since a fundamental characteristic of secure
+ connections is that they should be established with a successful
+ handshake.
+
+ Once encrypted, you use QSslSocket as a regular QTcpSocket. When
+ readyRead() is emitted, you can call read(), canReadLine() and
+ readLine(), or getChar() to read decrypted data from QSslSocket's
+ internal buffer, and you can call write() or putChar() to write
+ data back to the peer. QSslSocket will automatically encrypt the
+ written data for you, and emit encryptedBytesWritten() once
+ the data has been written to the peer.
+
+ As a convenience, QSslSocket supports QTcpSocket's blocking
+ functions waitForConnected(), waitForReadyRead(),
+ waitForBytesWritten(), and waitForDisconnected(). It also provides
+ waitForEncrypted(), which will block the calling thread until an
+ encrypted connection has been established.
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 2
+
+ QSslSocket provides an extensive, easy-to-use API for handling
+ cryptographic ciphers, private keys, and local, peer, and
+ Certification Authority (CA) certificates. It also provides an API
+ for handling errors that occur during the handshake phase.
+
+ The following features can also be customized:
+
+ \list
+ \o The socket's cryptographic cipher suite can be customized before
+ the handshake phase with setCiphers() and setDefaultCiphers().
+ \o The socket's local certificate and private key can be customized
+ before the handshake phase with setLocalCertificate() and
+ setPrivateKey().
+ \o The CA certificate database can be extended and customized with
+ addCaCertificate(), addCaCertificates(), setCaCertificates(),
+ addDefaultCaCertificate(), addDefaultCaCertificates(), and
+ 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.
+
+ This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (\l{http://www.openssl.org/}).
+
+ \note Be aware of the difference between the bytesWritten() signal and
+ the encryptedBytesWritten() signal. For a QTcpSocket, bytesWritten()
+ will get emitted as soon as data has been written to the TCP socket.
+ For a QSslSocket, bytesWritten() will get emitted when the data
+ is being encrypted and encryptedBytesWritten()
+ will get emitted as soon as data has been written to the TCP socket.
+
+ \section1 Symbian Platform Security Requirements
+
+ On Symbian, processes which use this class must have the
+ \c NetworkServices platform security capability. If the client
+ process lacks this capability, operations will fail.
+
+ Platform security capabilities are added via the
+ \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY}
+ qmake variable.
+
+ \sa QSslCertificate, QSslCipher, QSslError
+*/
+
+/*!
+ \enum QSslSocket::SslMode
+
+ Describes the connection modes available for QSslSocket.
+
+ \value UnencryptedMode The socket is unencrypted. Its
+ behavior is identical to QTcpSocket.
+
+ \value SslClientMode The socket is a client-side SSL socket.
+ It is either alreayd encrypted, or it is in the SSL handshake
+ phase (see QSslSocket::isEncrypted()).
+
+ \value SslServerMode The socket is a server-side SSL socket.
+ It is either already encrypted, or it is in the SSL handshake
+ phase (see QSslSocket::isEncrypted()).
+*/
+
+/*!
+ \enum QSslSocket::PeerVerifyMode
+ \since 4.4
+
+ Describes the peer verification modes for QSslSocket. The default mode is
+ AutoVerifyPeer, which selects an appropriate mode depending on the
+ socket's QSocket::SslMode.
+
+ \value VerifyNone QSslSocket will not request a certificate from the
+ peer. You can set this mode if you are not interested in the identity of
+ the other side of the connection. The connection will still be encrypted,
+ and your socket will still send its local certificate to the peer if it's
+ requested.
+
+ \value QueryPeer QSslSocket will request a certificate from the peer, but
+ does not require this certificate to be valid. This is useful when you
+ want to display peer certificate details to the user without affecting the
+ actual SSL handshake. This mode is the default for servers.
+
+ \value VerifyPeer QSslSocket will request a certificate from the peer
+ during the SSL handshake phase, and requires that this certificate is
+ valid. On failure, QSslSocket will emit the QSslSocket::sslErrors()
+ signal. This mode is the default for clients.
+
+ \value AutoVerifyPeer QSslSocket will automatically use QueryPeer for
+ server sockets and VerifyPeer for client sockets.
+
+ \sa QSslSocket::peerVerifyMode()
+*/
+
+/*!
+ \fn QSslSocket::encrypted()
+
+ This signal is emitted when QSslSocket enters encrypted mode. After this
+ signal has been emitted, QSslSocket::isEncrypted() will return true, and
+ all further transmissions on the socket will be encrypted.
+
+ \sa QSslSocket::connectToHostEncrypted(), QSslSocket::isEncrypted()
+*/
+
+/*!
+ \fn QSslSocket::modeChanged(QSslSocket::SslMode mode)
+
+ This signal is emitted when QSslSocket changes from \l
+ QSslSocket::UnencryptedMode to either \l QSslSocket::SslClientMode or \l
+ QSslSocket::SslServerMode. \a mode is the new mode.
+
+ \sa QSslSocket::mode()
+*/
+
+/*!
+ \fn QSslSocket::encryptedBytesWritten(qint64 written)
+ \since 4.4
+
+ This signal is emitted when QSslSocket writes its encrypted data to the
+ network. The \a written parameter contains the number of bytes that were
+ successfully written.
+
+ \sa QIODevice::bytesWritten()
+*/
+
+/*!
+ \fn void QSslSocket::peerVerifyError(const QSslError &error)
+ \since 4.4
+
+ QSslSocket can emit this signal several times during the SSL handshake,
+ before encryption has been established, to indicate that an error has
+ occurred while establishing the identity of the peer. The \a error is
+ usually an indication that QSslSocket is unable to securely identify the
+ peer.
+
+ This signal provides you with an early indication when something's wrong.
+ By connecting to this signal, you can manually choose to tear down the
+ connection from inside the connected slot before the handshake has
+ completed. If no action is taken, QSslSocket will proceed to emitting
+ QSslSocket::sslErrors().
+
+ \sa sslErrors()
+*/
+
+/*!
+ \fn void QSslSocket::sslErrors(const QList<QSslError> &errors);
+
+ QSslSocket emits this signal after the SSL handshake to indicate that one
+ or more errors have occurred while establishing the identity of the
+ peer. The errors are usually an indication that QSslSocket is unable to
+ securely identify the peer. Unless any action is taken, the connection
+ will be dropped after this signal has been emitted.
+
+ If you want to continue connecting despite the errors that have occurred,
+ you must call QSslSocket::ignoreSslErrors() from inside a slot connected to
+ this signal. If you need to access the error list at a later point, you
+ can call sslErrors() (without arguments).
+
+ \a errors contains one or more errors that prevent QSslSocket from
+ verifying the identity of the peer.
+
+ Note: You cannot use Qt::QueuedConnection when connecting to this signal,
+ or calling QSslSocket::ignoreSslErrors() will have no effect.
+
+ \sa peerVerifyError()
+*/
+
+#include "qsslcipher.h"
+#include "qsslsocket.h"
+#include "qsslsocket_openssl_p.h"
+#include "qsslconfiguration_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qhostinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+class QSslSocketGlobalData
+{
+public:
+ QSslSocketGlobalData() : config(new QSslConfigurationPrivate) {}
+
+ QMutex mutex;
+ QList<QSslCipher> supportedCiphers;
+ QExplicitlySharedDataPointer<QSslConfigurationPrivate> config;
+};
+Q_GLOBAL_STATIC(QSslSocketGlobalData, globalData)
+
+/*!
+ Constructs a QSslSocket object. \a parent is passed to QObject's
+ constructor. The new socket's \l {QSslCipher} {cipher} suite is
+ set to the one returned by the static method defaultCiphers().
+*/
+QSslSocket::QSslSocket(QObject *parent)
+ : QTcpSocket(*new QSslSocketBackendPrivate, parent)
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::QSslSocket(" << parent << "), this =" << (void *)this;
+#endif
+ d->q_ptr = this;
+ d->init();
+}
+
+/*!
+ Destroys the QSslSocket.
+*/
+QSslSocket::~QSslSocket()
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::~QSslSocket(), this =" << (void *)this;
+#endif
+ delete d->plainSocket;
+ d->plainSocket = 0;
+}
+
+/*!
+ Starts an encrypted connection to the device \a hostName on \a
+ port, using \a mode as the \l OpenMode. This is equivalent to
+ calling connectToHost() to establish the connection, followed by a
+ call to startClientEncryption().
+
+ QSslSocket first enters the HostLookupState. Then, after entering
+ either the event loop or one of the waitFor...() functions, it
+ enters the ConnectingState, emits connected(), and then initiates
+ the SSL client handshake. At each state change, QSslSocket emits
+ signal stateChanged().
+
+ After initiating the SSL client handshake, if the identity of the
+ peer can't be established, signal sslErrors() is emitted. If you
+ want to ignore the errors and continue connecting, you must call
+ ignoreSslErrors(), either from inside a slot function connected to
+ the sslErrors() signal, or prior to entering encrypted mode. If
+ ignoreSslErrors() is not called, the connection is dropped, signal
+ disconnected() is emitted, and QSslSocket returns to the
+ UnconnectedState.
+
+ If the SSL handshake is successful, QSslSocket emits encrypted().
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 3
+
+ \bold{Note:} The example above shows that text can be written to
+ the socket immediately after requesting the encrypted connection,
+ before the encrypted() signal has been emitted. In such cases, the
+ text is queued in the object and written to the socket \e after
+ the connection is established and the encrypted() signal has been
+ emitted.
+
+ The default for \a mode is \l ReadWrite.
+
+ If you want to create a QSslSocket on the server side of a connection, you
+ should instead call startServerEncryption() upon receiving the incoming
+ connection through QTcpServer.
+
+ \sa connectToHost(), startClientEncryption(), waitForConnected(), waitForEncrypted()
+*/
+void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode)
+{
+ Q_D(QSslSocket);
+ if (d->state == ConnectedState || d->state == ConnectingState) {
+ qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected");
+ return;
+ }
+
+ d->init();
+ d->autoStartHandshake = true;
+ d->initialized = true;
+
+ // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs)
+ // establish the connection immediately (i.e., first attempt).
+ connectToHost(hostName, port, mode);
+}
+
+/*!
+ \since 4.6
+ \overload
+
+ In addition to the original behaviour of connectToHostEncrypted,
+ this overloaded method enables the usage of a different hostname
+ (\a sslPeerName) for the certificate validation instead of
+ the one used for the TCP connection (\a hostName).
+
+ \sa connectToHostEncrypted()
+*/
+void QSslSocket::connectToHostEncrypted(const QString &hostName, quint16 port,
+ const QString &sslPeerName, OpenMode mode)
+{
+ Q_D(QSslSocket);
+ if (d->state == ConnectedState || d->state == ConnectingState) {
+ qWarning("QSslSocket::connectToHostEncrypted() called when already connecting/connected");
+ return;
+ }
+
+ d->init();
+ d->autoStartHandshake = true;
+ d->initialized = true;
+ d->verificationPeerName = sslPeerName;
+
+ // Note: When connecting to localhost, some platforms (e.g., HP-UX and some BSDs)
+ // establish the connection immediately (i.e., first attempt).
+ connectToHost(hostName, port, mode);
+}
+
+/*!
+ Initializes QSslSocket with the native socket descriptor \a
+ socketDescriptor. Returns true if \a socketDescriptor is accepted
+ as a valid socket descriptor; otherwise returns false.
+ The socket is opened in the mode specified by \a openMode, and
+ enters the socket state specified by \a state.
+
+ \bold{Note:} It is not possible to initialize two sockets with the same
+ native socket descriptor.
+
+ \sa socketDescriptor()
+*/
+bool QSslSocket::setSocketDescriptor(int socketDescriptor, SocketState state, OpenMode openMode)
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::setSocketDescriptor(" << socketDescriptor << ','
+ << state << ',' << openMode << ')';
+#endif
+ if (!d->plainSocket)
+ d->createPlainSocket(openMode);
+ bool retVal = d->plainSocket->setSocketDescriptor(socketDescriptor, state, openMode);
+ d->cachedSocketDescriptor = d->plainSocket->socketDescriptor();
+ setSocketError(d->plainSocket->error());
+ setSocketState(state);
+ setOpenMode(openMode);
+ setLocalPort(d->plainSocket->localPort());
+ setLocalAddress(d->plainSocket->localAddress());
+ setPeerPort(d->plainSocket->peerPort());
+ setPeerAddress(d->plainSocket->peerAddress());
+ setPeerName(d->plainSocket->peerName());
+ return retVal;
+}
+
+/*!
+ \since 4.6
+ Sets the given \a option to the value described by \a value.
+
+ \sa socketOption()
+*/
+void QSslSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
+{
+ Q_D(QSslSocket);
+ if (d->plainSocket)
+ d->plainSocket->setSocketOption(option, value);
+}
+
+/*!
+ \since 4.6
+ Returns the value of the \a option option.
+
+ \sa setSocketOption()
+*/
+QVariant QSslSocket::socketOption(QAbstractSocket::SocketOption option)
+{
+ Q_D(QSslSocket);
+ if (d->plainSocket)
+ return d->plainSocket->socketOption(option);
+ else
+ return QVariant();
+}
+
+/*!
+ Returns the current mode for the socket; either UnencryptedMode, where
+ QSslSocket behaves identially to QTcpSocket, or one of SslClientMode or
+ SslServerMode, where the client is either negotiating or in encrypted
+ mode.
+
+ When the mode changes, QSslSocket emits modeChanged()
+
+ \sa SslMode
+*/
+QSslSocket::SslMode QSslSocket::mode() const
+{
+ Q_D(const QSslSocket);
+ return d->mode;
+}
+
+/*!
+ Returns true if the socket is encrypted; otherwise, false is returned.
+
+ An encrypted socket encrypts all data that is written by calling write()
+ or putChar() before the data is written to the network, and decrypts all
+ incoming data as the data is received from the network, before you call
+ read(), readLine() or getChar().
+
+ QSslSocket emits encrypted() when it enters encrypted mode.
+
+ You can call sessionCipher() to find which cryptographic cipher is used to
+ encrypt and decrypt your data.
+
+ \sa mode()
+*/
+bool QSslSocket::isEncrypted() const
+{
+ Q_D(const QSslSocket);
+ return d->connectionEncrypted;
+}
+
+/*!
+ Returns the socket's SSL protocol. By default, \l QSsl::SecureProtocols is used.
+
+ \sa setProtocol()
+*/
+QSsl::SslProtocol QSslSocket::protocol() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.protocol;
+}
+
+/*!
+ Sets the socket's SSL protocol to \a protocol. This will affect the next
+ initiated handshake; calling this function on an already-encrypted socket
+ will not affect the socket's protocol.
+*/
+void QSslSocket::setProtocol(QSsl::SslProtocol protocol)
+{
+ Q_D(QSslSocket);
+ d->configuration.protocol = protocol;
+}
+
+/*!
+ \since 4.4
+
+ Returns the socket's verify mode. This mode mode decides whether
+ QSslSocket should request a certificate from the peer (i.e., the client
+ requests a certificate from the server, or a server requesting a
+ certificate from the client), and whether it should require that this
+ certificate is valid.
+
+ The default mode is AutoVerifyPeer, which tells QSslSocket to use
+ VerifyPeer for clients and QueryPeer for servers.
+
+ \sa setPeerVerifyMode(), peerVerifyDepth(), mode()
+*/
+QSslSocket::PeerVerifyMode QSslSocket::peerVerifyMode() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.peerVerifyMode;
+}
+
+/*!
+ \since 4.4
+
+ Sets the socket's verify mode to \a mode. This mode decides whether
+ QSslSocket should request a certificate from the peer (i.e., the client
+ requests a certificate from the server, or a server requesting a
+ certificate from the client), and whether it should require that this
+ certificate is valid.
+
+ The default mode is AutoVerifyPeer, which tells QSslSocket to use
+ VerifyPeer for clients and QueryPeer for servers.
+
+ Setting this mode after encryption has started has no effect on the
+ current connection.
+
+ \sa peerVerifyMode(), setPeerVerifyDepth(), mode()
+*/
+void QSslSocket::setPeerVerifyMode(QSslSocket::PeerVerifyMode mode)
+{
+ Q_D(QSslSocket);
+ d->configuration.peerVerifyMode = mode;
+}
+
+/*!
+ \since 4.4
+
+ Returns the maximum number of certificates in the peer's certificate chain
+ to be checked during the SSL handshake phase, or 0 (the default) if no
+ maximum depth has been set, indicating that the whole certificate chain
+ should be checked.
+
+ The certificates are checked in issuing order, starting with the peer's
+ own certificate, then its issuer's certificate, and so on.
+
+ \sa setPeerVerifyDepth(), peerVerifyMode()
+*/
+int QSslSocket::peerVerifyDepth() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.peerVerifyDepth;
+}
+
+/*!
+ \since 4.4
+
+ Sets the maximum number of certificates in the peer's certificate chain to
+ be checked during the SSL handshake phase, to \a depth. Setting a depth of
+ 0 means that no maximum depth is set, indicating that the whole
+ certificate chain should be checked.
+
+ The certificates are checked in issuing order, starting with the peer's
+ own certificate, then its issuer's certificate, and so on.
+
+ \sa peerVerifyDepth(), setPeerVerifyMode()
+*/
+void QSslSocket::setPeerVerifyDepth(int depth)
+{
+ Q_D(QSslSocket);
+ if (depth < 0) {
+ qWarning("QSslSocket::setPeerVerifyDepth: cannot set negative depth of %d", depth);
+ return;
+ }
+ d->configuration.peerVerifyDepth = 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 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
+ reading.
+*/
+qint64 QSslSocket::bytesAvailable() const
+{
+ Q_D(const QSslSocket);
+ if (d->mode == UnencryptedMode)
+ return QIODevice::bytesAvailable() + (d->plainSocket ? d->plainSocket->bytesAvailable() : 0);
+ return QIODevice::bytesAvailable() + d->readBuffer.size();
+}
+
+/*!
+ \reimp
+
+ Returns the number of unencrypted bytes that are waiting to be encrypted
+ and written to the network.
+*/
+qint64 QSslSocket::bytesToWrite() const
+{
+ Q_D(const QSslSocket);
+ if (d->mode == UnencryptedMode)
+ return d->plainSocket ? d->plainSocket->bytesToWrite() : 0;
+ return d->writeBuffer.size();
+}
+
+/*!
+ \since 4.4
+
+ Returns the number of encrypted bytes that are awaiting decryption.
+ Normally, this function will return 0 because QSslSocket decrypts its
+ incoming data as soon as it can.
+*/
+qint64 QSslSocket::encryptedBytesAvailable() const
+{
+ Q_D(const QSslSocket);
+ if (d->mode == UnencryptedMode)
+ return 0;
+ return d->plainSocket->bytesAvailable();
+}
+
+/*!
+ \since 4.4
+
+ Returns the number of encrypted bytes that are waiting to be written to
+ the network.
+*/
+qint64 QSslSocket::encryptedBytesToWrite() const
+{
+ Q_D(const QSslSocket);
+ if (d->mode == UnencryptedMode)
+ return 0;
+ return d->plainSocket->bytesToWrite();
+}
+
+/*!
+ \reimp
+
+ Returns true if you can read one while line (terminated by a single ASCII
+ '\n' character) of decrypted characters; otherwise, false is returned.
+*/
+bool QSslSocket::canReadLine() const
+{
+ Q_D(const QSslSocket);
+ if (d->mode == UnencryptedMode)
+ return QIODevice::canReadLine() || (d->plainSocket && d->plainSocket->canReadLine());
+ return QIODevice::canReadLine() || (!d->readBuffer.isEmpty() && d->readBuffer.canReadLine());
+}
+
+/*!
+ \reimp
+*/
+void QSslSocket::close()
+{
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::close()";
+#endif
+ Q_D(QSslSocket);
+ if (d->plainSocket)
+ d->plainSocket->close();
+ QTcpSocket::close();
+
+ // must be cleared, reading/writing not possible on closed socket:
+ d->readBuffer.clear();
+ d->writeBuffer.clear();
+ // for QTcpSocket this is already done because it uses the readBuffer/writeBuffer
+ // if the QIODevice it is based on
+ // ### FIXME QSslSocket should probably do similar instead of having
+ // its own readBuffer/writeBuffer
+}
+
+/*!
+ \reimp
+*/
+bool QSslSocket::atEnd() const
+{
+ Q_D(const QSslSocket);
+ if (d->mode == UnencryptedMode)
+ return QIODevice::atEnd() && (!d->plainSocket || d->plainSocket->atEnd());
+ return QIODevice::atEnd() && d->readBuffer.isEmpty();
+}
+
+/*!
+ This function writes as much as possible from the internal write buffer to
+ the underlying network socket, without blocking. If any data was written,
+ this function returns true; otherwise false is returned.
+
+ Call this function if you need QSslSocket to start sending buffered data
+ immediately. The number of bytes successfully written depends on the
+ operating system. In most cases, you do not need to call this function,
+ because QAbstractSocket will start sending data automatically once control
+ goes back to the event loop. In the absence of an event loop, call
+ waitForBytesWritten() instead.
+
+ \sa write(), waitForBytesWritten()
+*/
+// Note! docs copied from QAbstractSocket::flush()
+bool QSslSocket::flush()
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::flush()";
+#endif
+ if (d->mode != UnencryptedMode)
+ // encrypt any unencrypted bytes in our buffer
+ d->transmit();
+
+ return d->plainSocket ? d->plainSocket->flush() : false;
+}
+
+/*!
+ \since 4.4
+
+ Sets the size of QSslSocket's internal read buffer to be \a size bytes.
+*/
+void QSslSocket::setReadBufferSize(qint64 size)
+{
+ Q_D(QSslSocket);
+ d->readBufferMaxSize = size;
+
+ if (d->plainSocket)
+ d->plainSocket->setReadBufferSize(size);
+}
+
+/*!
+ Aborts the current connection and resets the socket. Unlike
+ disconnectFromHost(), this function immediately closes the socket,
+ clearing any pending data in the write buffer.
+
+ \sa disconnectFromHost(), close()
+*/
+void QSslSocket::abort()
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::abort()";
+#endif
+ if (d->plainSocket)
+ d->plainSocket->abort();
+ close();
+}
+
+/*!
+ \since 4.4
+
+ Returns the socket's SSL configuration state. The default SSL
+ configuration of a socket is to use the default ciphers,
+ default CA certificates, no local private key or certificate.
+
+ The SSL configuration also contains fields that can change with
+ time without notice.
+
+ \sa localCertificate(), peerCertificate(), peerCertificateChain(),
+ sessionCipher(), privateKey(), ciphers(), caCertificates()
+*/
+QSslConfiguration QSslSocket::sslConfiguration() const
+{
+ Q_D(const QSslSocket);
+
+ // create a deep copy of our configuration
+ QSslConfigurationPrivate *copy = new QSslConfigurationPrivate(d->configuration);
+ copy->ref = 0; // the QSslConfiguration constructor refs up
+ copy->sessionCipher = d->sessionCipher();
+
+ return QSslConfiguration(copy);
+}
+
+/*!
+ \since 4.4
+
+ Sets the socket's SSL configuration to be the contents of \a configuration.
+ This function sets the local certificate, the ciphers, the private key and the CA
+ certificates to those stored in \a configuration.
+
+ It is not possible to set the SSL-state related fields.
+
+ \sa setLocalCertificate(), setPrivateKey(), setCaCertificates(), setCiphers()
+*/
+void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
+{
+ Q_D(QSslSocket);
+ d->configuration.localCertificate = configuration.localCertificate();
+ d->configuration.privateKey = configuration.privateKey();
+ d->configuration.ciphers = configuration.ciphers();
+ d->configuration.caCertificates = configuration.caCertificates();
+ d->configuration.peerVerifyDepth = configuration.peerVerifyDepth();
+ d->configuration.peerVerifyMode = configuration.peerVerifyMode();
+ d->configuration.protocol = configuration.protocol();
+ d->allowRootCertOnDemandLoading = false;
+}
+
+/*!
+ Sets the socket's local certificate to \a certificate. The local
+ certificate is necessary if you need to confirm your identity to the
+ peer. It is used together with the private key; if you set the local
+ certificate, you must also set the private key.
+
+ The local certificate and private key are always necessary for server
+ sockets, but are also rarely used by client sockets if the server requires
+ the client to authenticate.
+
+ \sa localCertificate(), setPrivateKey()
+*/
+void QSslSocket::setLocalCertificate(const QSslCertificate &certificate)
+{
+ Q_D(QSslSocket);
+ d->configuration.localCertificate = certificate;
+}
+
+/*!
+ \overload
+
+ Sets the socket's local \l {QSslCertificate} {certificate} to the
+ first one found in file \a path, which is parsed according to the
+ specified \a format.
+*/
+void QSslSocket::setLocalCertificate(const QString &path,
+ QSsl::EncodingFormat format)
+{
+ Q_D(QSslSocket);
+ QFile file(path);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text))
+ d->configuration.localCertificate = QSslCertificate(file.readAll(), format);
+}
+
+/*!
+ Returns the socket's local \l {QSslCertificate} {certificate}, or
+ an empty certificate if no local certificate has been assigned.
+
+ \sa setLocalCertificate(), privateKey()
+*/
+QSslCertificate QSslSocket::localCertificate() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.localCertificate;
+}
+
+/*!
+ Returns the peer's digital certificate (i.e., the immediate
+ certificate of the host you are connected to), or a null
+ certificate, if the peer has not assigned a certificate.
+
+ The peer certificate is checked automatically during the
+ handshake phase, so this function is normally used to fetch
+ the certificate for display or for connection diagnostic
+ purposes. It contains information about the peer, including
+ its host name, the certificate issuer, and the peer's public
+ key.
+
+ Because the peer certificate is set during the handshake phase, it
+ is safe to access the peer certificate from a slot connected to
+ the sslErrors() signal or the encrypted() signal.
+
+ If a null certificate is returned, it can mean the SSL handshake
+ failed, or it can mean the host you are connected to doesn't have
+ a certificate, or it can mean there is no connection.
+
+ If you want to check the peer's complete chain of certificates,
+ use peerCertificateChain() to get them all at once.
+
+ \sa peerCertificateChain()
+*/
+QSslCertificate QSslSocket::peerCertificate() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.peerCertificate;
+}
+
+/*!
+ Returns the peer's chain of digital certificates, or an empty list
+ of certificates.
+
+ Peer certificates are checked automatically during the handshake
+ phase. This function is normally used to fetch certificates for
+ display, or for performing connection diagnostics. Certificates
+ contain information about the peer and the certificate issuers,
+ including host name, issuer names, and issuer public keys.
+
+ The peer certificates are set in QSslSocket during the handshake
+ phase, so it is safe to call this function from a slot connected
+ to the sslErrors() signal or the encrypted() signal.
+
+ If an empty list is returned, it can mean the SSL handshake
+ failed, or it can mean the host you are connected to doesn't have
+ a certificate, or it can mean there is no connection.
+
+ If you want to get only the peer's immediate certificate, use
+ peerCertificate().
+
+ \sa peerCertificate()
+*/
+QList<QSslCertificate> QSslSocket::peerCertificateChain() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.peerCertificateChain;
+}
+
+/*!
+ Returns the socket's cryptographic \l {QSslCipher} {cipher}, or a
+ null cipher if the connection isn't encrypted. The socket's cipher
+ for the session is set during the handshake phase. The cipher is
+ used to encrypt and decrypt data transmitted through the socket.
+
+ QSslSocket also provides functions for setting the ordered list of
+ ciphers from which the handshake phase will eventually select the
+ session cipher. This ordered list must be in place before the
+ handshake phase begins.
+
+ \sa ciphers(), setCiphers(), setDefaultCiphers(), defaultCiphers(),
+ supportedCiphers()
+*/
+QSslCipher QSslSocket::sessionCipher() const
+{
+ Q_D(const QSslSocket);
+ return d->sessionCipher();
+}
+
+/*!
+ Sets the socket's private \l {QSslKey} {key} to \a key. The
+ private key and the local \l {QSslCertificate} {certificate} are
+ used by clients and servers that must prove their identity to
+ SSL peers.
+
+ Both the key and the local certificate are required if you are
+ creating an SSL server socket. If you are creating an SSL client
+ socket, the key and local certificate are required if your client
+ must identify itself to an SSL server.
+
+ \sa privateKey(), setLocalCertificate()
+*/
+void QSslSocket::setPrivateKey(const QSslKey &key)
+{
+ Q_D(QSslSocket);
+ d->configuration.privateKey = key;
+}
+
+/*!
+ \overload
+
+ Reads the string in file \a fileName and decodes it using
+ a specified \a algorithm and encoding \a format to construct
+ an \l {QSslKey} {SSL key}. If the encoded key is encrypted,
+ \a passPhrase is used to decrypt it.
+
+ The socket's private key is set to the constructed key. The
+ private key and the local \l {QSslCertificate} {certificate} are
+ used by clients and servers that must prove their identity to SSL
+ peers.
+
+ Both the key and the local certificate are required if you are
+ creating an SSL server socket. If you are creating an SSL client
+ socket, the key and local certificate are required if your client
+ must identify itself to an SSL server.
+
+ \sa privateKey(), setLocalCertificate()
+*/
+void QSslSocket::setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm,
+ QSsl::EncodingFormat format, const QByteArray &passPhrase)
+{
+ Q_D(QSslSocket);
+ QFile file(fileName);
+ if (file.open(QIODevice::ReadOnly)) {
+ d->configuration.privateKey = QSslKey(file.readAll(), algorithm,
+ format, QSsl::PrivateKey, passPhrase);
+ }
+}
+
+/*!
+ Returns this socket's private key.
+
+ \sa setPrivateKey(), localCertificate()
+*/
+QSslKey QSslSocket::privateKey() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.privateKey;
+}
+
+/*!
+ Returns this socket's current cryptographic cipher suite. This
+ list is used during the socket's handshake phase for choosing a
+ session cipher. The returned list of ciphers is ordered by
+ descending preference. (i.e., the first cipher in the list is the
+ most preferred cipher). The session cipher will be the first one
+ in the list that is also supported by the peer.
+
+ By default, the handshake phase can choose any of the ciphers
+ supported by this system's SSL libraries, which may vary from
+ system to system. The list of ciphers supported by this system's
+ SSL libraries is returned by supportedCiphers(). You can restrict
+ the list of ciphers used for choosing the session cipher for this
+ socket by calling setCiphers() with a subset of the supported
+ ciphers. You can revert to using the entire set by calling
+ setCiphers() with the list returned by supportedCiphers().
+
+ You can restrict the list of ciphers used for choosing the session
+ cipher for \e all sockets by calling setDefaultCiphers() with a
+ subset of the supported ciphers. You can revert to using the
+ entire set by calling setCiphers() with the list returned by
+ supportedCiphers().
+
+ \sa setCiphers(), defaultCiphers(), setDefaultCiphers(), supportedCiphers()
+*/
+QList<QSslCipher> QSslSocket::ciphers() const
+{
+ Q_D(const QSslSocket);
+ return d->configuration.ciphers;
+}
+
+/*!
+ Sets the cryptographic cipher suite for this socket to \a ciphers,
+ which must contain a subset of the ciphers in the list returned by
+ supportedCiphers().
+
+ Restricting the cipher suite must be done before the handshake
+ phase, where the session cipher is chosen.
+
+ \sa ciphers(), setDefaultCiphers(), supportedCiphers()
+*/
+void QSslSocket::setCiphers(const QList<QSslCipher> &ciphers)
+{
+ Q_D(QSslSocket);
+ d->configuration.ciphers = ciphers;
+}
+
+/*!
+ Sets the cryptographic cipher suite for this socket to \a ciphers, which
+ is a colon-separated list of cipher suite names. The ciphers are listed in
+ order of preference, starting with the most preferred cipher. For example:
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 4
+
+ Each cipher name in \a ciphers must be the name of a cipher in the
+ list returned by supportedCiphers(). Restricting the cipher suite
+ must be done before the handshake phase, where the session cipher
+ is chosen.
+
+ \sa ciphers(), setDefaultCiphers(), supportedCiphers()
+*/
+void QSslSocket::setCiphers(const QString &ciphers)
+{
+ Q_D(QSslSocket);
+ d->configuration.ciphers.clear();
+ foreach (const QString &cipherName, ciphers.split(QLatin1String(":"),QString::SkipEmptyParts)) {
+ for (int i = 0; i < 3; ++i) {
+ // ### Crude
+ QSslCipher cipher(cipherName, QSsl::SslProtocol(i));
+ if (!cipher.isNull())
+ d->configuration.ciphers << cipher;
+ }
+ }
+}
+
+/*!
+ Sets the default cryptographic cipher suite for all sockets in
+ this application to \a ciphers, which must contain a subset of the
+ ciphers in the list returned by supportedCiphers().
+
+ Restricting the default cipher suite only affects SSL sockets
+ that perform their handshake phase after the default cipher
+ suite has been changed.
+
+ \sa setCiphers(), defaultCiphers(), supportedCiphers()
+*/
+void QSslSocket::setDefaultCiphers(const QList<QSslCipher> &ciphers)
+{
+ QSslSocketPrivate::setDefaultCiphers(ciphers);
+}
+
+/*!
+ Returns the default cryptographic cipher suite for all sockets in
+ this application. This list is used during the socket's handshake
+ phase when negotiating with the peer to choose a session cipher.
+ The list is ordered by preference (i.e., the first cipher in the
+ list is the most preferred cipher).
+
+ By default, the handshake phase can choose any of the ciphers
+ supported by this system's SSL libraries, which may vary from
+ system to system. The list of ciphers supported by this system's
+ SSL libraries is returned by supportedCiphers().
+
+ \sa supportedCiphers()
+*/
+QList<QSslCipher> QSslSocket::defaultCiphers()
+{
+ return QSslSocketPrivate::defaultCiphers();
+}
+
+/*!
+ Returns the list of cryptographic ciphers supported by this
+ system. This list is set by the system's SSL libraries and may
+ vary from system to system.
+
+ \sa defaultCiphers(), ciphers(), setCiphers()
+*/
+QList<QSslCipher> QSslSocket::supportedCiphers()
+{
+ return QSslSocketPrivate::supportedCiphers();
+}
+
+/*!
+ Searches all files in the \a path for certificates encoded in the
+ specified \a format and adds them to this socket's CA certificate
+ database. \a path can be explicit, or it can contain wildcards in
+ the format specified by \a syntax. Returns true if one or more
+ certificates are added to the socket's CA certificate database;
+ otherwise returns false.
+
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ For more precise control, use addCaCertificate().
+
+ \sa addCaCertificate(), QSslCertificate::fromPath()
+*/
+bool QSslSocket::addCaCertificates(const QString &path, QSsl::EncodingFormat format,
+ QRegExp::PatternSyntax syntax)
+{
+ Q_D(QSslSocket);
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
+ if (certs.isEmpty())
+ return false;
+
+ d->configuration.caCertificates += certs;
+ return true;
+}
+
+/*!
+ Adds the \a certificate to this socket's CA certificate database.
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ To add multiple certificates, use addCaCertificates().
+
+ \sa caCertificates(), setCaCertificates()
+*/
+void QSslSocket::addCaCertificate(const QSslCertificate &certificate)
+{
+ Q_D(QSslSocket);
+ d->configuration.caCertificates += certificate;
+}
+
+/*!
+ Adds the \a certificates to this socket's CA certificate database.
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ For more precise control, use addCaCertificate().
+
+ \sa caCertificates(), addDefaultCaCertificate()
+*/
+void QSslSocket::addCaCertificates(const QList<QSslCertificate> &certificates)
+{
+ Q_D(QSslSocket);
+ d->configuration.caCertificates += certificates;
+}
+
+/*!
+ Sets this socket's CA certificate database to be \a certificates.
+ The certificate database must be set prior to the SSL handshake.
+ The CA certificate database is used by the socket during the
+ handshake phase to validate the peer's certificate.
+
+ The CA certificate database can be reset to the current default CA
+ certificate database by calling this function with the list of CA
+ certificates returned by defaultCaCertificates().
+
+ \sa defaultCaCertificates()
+*/
+void QSslSocket::setCaCertificates(const QList<QSslCertificate> &certificates)
+{
+ Q_D(QSslSocket);
+ d->configuration.caCertificates = certificates;
+ d->allowRootCertOnDemandLoading = false;
+}
+
+/*!
+ Returns this socket's CA certificate database. The CA certificate
+ database is used by the socket during the handshake phase to
+ validate the peer's certificate. It can be moodified prior to the
+ 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
+{
+ Q_D(const QSslSocket);
+ return d->configuration.caCertificates;
+}
+
+/*!
+ Searches all files in the \a path for certificates with the
+ specified \a encoding and adds them to the default CA certificate
+ database. \a path can be an explicit file, or it can contain
+ wildcards in the format specified by \a syntax. Returns true if
+ any CA certificates are added to the default database.
+
+ Each SSL socket's CA certificate database is initialized to the
+ default CA certificate database.
+
+ \sa defaultCaCertificates(), addCaCertificates(), addDefaultCaCertificate()
+*/
+bool QSslSocket::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat encoding,
+ QRegExp::PatternSyntax syntax)
+{
+ return QSslSocketPrivate::addDefaultCaCertificates(path, encoding, syntax);
+}
+
+/*!
+ Adds \a certificate to the default CA certificate database. Each
+ SSL socket's CA certificate database is initialized to the default
+ CA certificate database.
+
+ \sa defaultCaCertificates(), addCaCertificates()
+*/
+void QSslSocket::addDefaultCaCertificate(const QSslCertificate &certificate)
+{
+ QSslSocketPrivate::addDefaultCaCertificate(certificate);
+}
+
+/*!
+ Adds \a certificates to the default CA certificate database. Each
+ SSL socket's CA certificate database is initialized to the default
+ CA certificate database.
+
+ \sa defaultCaCertificates(), addCaCertificates()
+*/
+void QSslSocket::addDefaultCaCertificates(const QList<QSslCertificate> &certificates)
+{
+ QSslSocketPrivate::addDefaultCaCertificates(certificates);
+}
+
+/*!
+ 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. 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.
+
+ \sa addDefaultCaCertificate()
+*/
+void QSslSocket::setDefaultCaCertificates(const QList<QSslCertificate> &certificates)
+{
+ QSslSocketPrivate::setDefaultCaCertificates(certificates);
+}
+
+/*!
+ Returns the current default CA certificate database. This database
+ is originally set to your system's default CA certificate database.
+ If no system default database is found, an empty database will be
+ returned. You can override the default CA certificate database
+ with your own CA certificate database using setDefaultCaCertificates().
+
+ 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()
+{
+ return QSslSocketPrivate::defaultCaCertificates();
+}
+
+/*!
+ This function provides the CA certificate database
+ provided by the operating system. The CA certificate database
+ returned by this function is used to initialize the database
+ returned by defaultCaCertificates(). You can replace that database
+ with your own with setDefaultCaCertificates().
+
+ \sa caCertificates(), defaultCaCertificates(), setDefaultCaCertificates()
+*/
+QList<QSslCertificate> QSslSocket::systemCaCertificates()
+{
+ // we are calling ensureInitialized() in the method below
+ return QSslSocketPrivate::systemCaCertificates();
+}
+
+/*!
+ Waits until the socket is connected, or \a msecs milliseconds,
+ whichever happens first. If the connection has been established,
+ this function returns true; otherwise it returns false.
+
+ \sa QAbstractSocket::waitForConnected()
+*/
+bool QSslSocket::waitForConnected(int msecs)
+{
+ Q_D(QSslSocket);
+ if (!d->plainSocket)
+ return false;
+ bool retVal = d->plainSocket->waitForConnected(msecs);
+ if (!retVal) {
+ setSocketState(d->plainSocket->state());
+ setSocketError(d->plainSocket->error());
+ setErrorString(d->plainSocket->errorString());
+ }
+ return retVal;
+}
+
+/*!
+ Waits until the socket has completed the SSL handshake and has
+ emitted encrypted(), or \a msecs milliseconds, whichever comes
+ first. If encrypted() has been emitted, this function returns
+ true; otherwise (e.g., the socket is disconnected, or the SSL
+ handshake fails), false is returned.
+
+ The following example waits up to one second for the socket to be
+ encrypted:
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 5
+
+ If msecs is -1, this function will not time out.
+
+ \sa startClientEncryption(), startServerEncryption(), encrypted(), isEncrypted()
+*/
+bool QSslSocket::waitForEncrypted(int msecs)
+{
+ Q_D(QSslSocket);
+ if (!d->plainSocket || d->connectionEncrypted)
+ return false;
+ if (d->mode == UnencryptedMode && !d->autoStartHandshake)
+ return false;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ if (d->plainSocket->state() != QAbstractSocket::ConnectedState) {
+ // Wait until we've entered connected state.
+ if (!d->plainSocket->waitForConnected(msecs))
+ return false;
+ }
+
+ while (!d->connectionEncrypted) {
+ // Start the handshake, if this hasn't been started yet.
+ if (d->mode == UnencryptedMode)
+ startClientEncryption();
+ // Loop, waiting until the connection has been encrypted or an error
+ // occurs.
+ if (!d->plainSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed())))
+ return false;
+ }
+ return d->connectionEncrypted;
+}
+
+/*!
+ \reimp
+*/
+bool QSslSocket::waitForReadyRead(int msecs)
+{
+ Q_D(QSslSocket);
+ if (!d->plainSocket)
+ return false;
+ if (d->mode == UnencryptedMode && !d->autoStartHandshake)
+ return d->plainSocket->waitForReadyRead(msecs);
+
+ // This function must return true if and only if readyRead() *was* emitted.
+ // So we initialize "readyReadEmitted" to false and check if it was set to true.
+ // waitForReadyRead() could be called recursively, so we can't use the same variable
+ // (the inner waitForReadyRead() may fail, but the outer one still succeeded)
+ bool readyReadEmitted = false;
+ bool *previousReadyReadEmittedPointer = d->readyReadEmittedPointer;
+ d->readyReadEmittedPointer = &readyReadEmitted;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ if (!d->connectionEncrypted) {
+ // Wait until we've entered encrypted mode, or until a failure occurs.
+ if (!waitForEncrypted(msecs)) {
+ d->readyReadEmittedPointer = previousReadyReadEmittedPointer;
+ return false;
+ }
+ }
+
+ if (!d->writeBuffer.isEmpty()) {
+ // empty our cleartext write buffer first
+ d->transmit();
+ }
+
+ // test readyReadEmitted first because either operation above
+ // (waitForEncrypted or transmit) may have set it
+ while (!readyReadEmitted &&
+ d->plainSocket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
+ }
+
+ d->readyReadEmittedPointer = previousReadyReadEmittedPointer;
+ return readyReadEmitted;
+}
+
+/*!
+ \reimp
+*/
+bool QSslSocket::waitForBytesWritten(int msecs)
+{
+ Q_D(QSslSocket);
+ if (!d->plainSocket)
+ return false;
+ if (d->mode == UnencryptedMode)
+ return d->plainSocket->waitForBytesWritten(msecs);
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ if (!d->connectionEncrypted) {
+ // Wait until we've entered encrypted mode, or until a failure occurs.
+ if (!waitForEncrypted(msecs))
+ return false;
+ }
+ if (!d->writeBuffer.isEmpty()) {
+ // empty our cleartext write buffer first
+ d->transmit();
+ }
+
+ return d->plainSocket->waitForBytesWritten(qt_timeout_value(msecs, stopWatch.elapsed()));
+}
+
+/*!
+ Waits until the socket has disconnected or \a msecs milliseconds,
+ whichever comes first. If the connection has been disconnected,
+ this function returns true; otherwise it returns false.
+
+ \sa QAbstractSocket::waitForDisconnected()
+*/
+bool QSslSocket::waitForDisconnected(int msecs)
+{
+ Q_D(QSslSocket);
+
+ // require calling connectToHost() before waitForDisconnected()
+ if (state() == UnconnectedState) {
+ qWarning("QSslSocket::waitForDisconnected() is not allowed in UnconnectedState");
+ return false;
+ }
+
+ if (!d->plainSocket)
+ return false;
+ if (d->mode == UnencryptedMode)
+ return d->plainSocket->waitForDisconnected(msecs);
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ if (!d->connectionEncrypted) {
+ // Wait until we've entered encrypted mode, or until a failure occurs.
+ if (!waitForEncrypted(msecs))
+ return false;
+ }
+ bool retVal = d->plainSocket->waitForDisconnected(qt_timeout_value(msecs, stopWatch.elapsed()));
+ if (!retVal) {
+ setSocketState(d->plainSocket->state());
+ setSocketError(d->plainSocket->error());
+ setErrorString(d->plainSocket->errorString());
+ }
+ return retVal;
+}
+
+/*!
+ Returns a list of the last SSL errors that occurred. This is the
+ same list as QSslSocket passes via the sslErrors() signal. If the
+ connection has been encrypted with no errors, this function will
+ return an empty list.
+
+ \sa connectToHostEncrypted()
+*/
+QList<QSslError> QSslSocket::sslErrors() const
+{
+ Q_D(const QSslSocket);
+ return d->sslErrors;
+}
+
+/*!
+ Returns true if this platform supports SSL; otherwise, returns
+ false. If the platform doesn't support SSL, the socket will fail
+ in the connection phase.
+*/
+bool QSslSocket::supportsSsl()
+{
+ return QSslSocketPrivate::supportsSsl();
+}
+
+/*!
+ Starts a delayed SSL handshake for a client connection. This
+ function can be called when the socket is in the \l ConnectedState
+ but still in the \l UnencryptedMode. If it is not yet connected,
+ or if it is already encrypted, this function has no effect.
+
+ Clients that implement STARTTLS functionality often make use of
+ delayed SSL handshakes. Most other clients can avoid calling this
+ function directly by using connectToHostEncrypted() instead, which
+ automatically performs the handshake.
+
+ \sa connectToHostEncrypted(), startServerEncryption()
+*/
+void QSslSocket::startClientEncryption()
+{
+ Q_D(QSslSocket);
+ if (d->mode != UnencryptedMode) {
+ qWarning("QSslSocket::startClientEncryption: cannot start handshake on non-plain connection");
+ return;
+ }
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::startClientEncryption()";
+#endif
+ d->mode = SslClientMode;
+ emit modeChanged(d->mode);
+ d->startClientEncryption();
+}
+
+/*!
+ Starts a delayed SSL handshake for a server connection. This
+ function can be called when the socket is in the \l ConnectedState
+ but still in \l UnencryptedMode. If it is not connected or it is
+ already encrypted, the function has no effect.
+
+ For server sockets, calling this function is the only way to
+ initiate the SSL handshake. Most servers will call this function
+ immediately upon receiving a connection, or as a result of having
+ received a protocol-specific command to enter SSL mode (e.g, the
+ server may respond to receiving the string "STARTTLS\r\n" by
+ calling this function).
+
+ The most common way to implement an SSL server is to create a
+ subclass of QTcpServer and reimplement
+ QTcpServer::incomingConnection(). The returned socket descriptor
+ is then passed to QSslSocket::setSocketDescriptor().
+
+ \sa connectToHostEncrypted(), startClientEncryption()
+*/
+void QSslSocket::startServerEncryption()
+{
+ Q_D(QSslSocket);
+ if (d->mode != UnencryptedMode) {
+ qWarning("QSslSocket::startServerEncryption: cannot start handshake on non-plain connection");
+ return;
+ }
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::startServerEncryption()";
+#endif
+ d->mode = SslServerMode;
+ emit modeChanged(d->mode);
+ d->startServerEncryption();
+}
+
+/*!
+ This slot tells QSslSocket to ignore errors during QSslSocket's
+ handshake phase and continue connecting. If you want to continue
+ with the connection even if errors occur during the handshake
+ phase, then you must call this slot, either from a slot connected
+ to sslErrors(), or before the handshake phase. If you don't call
+ this slot, either in response to errors or before the handshake,
+ the connection will be dropped after the sslErrors() signal has
+ been emitted.
+
+ If there are no errors during the SSL handshake phase (i.e., the
+ identity of the peer is established with no problems), QSslSocket
+ will not emit the sslErrors() signal, and it is unnecessary to
+ call this function.
+
+ Ignoring errors that occur during an SSL handshake should be done
+ with caution. A fundamental characteristic of secure connections
+ is that they should be established with an error free handshake.
+
+ \sa sslErrors()
+*/
+void QSslSocket::ignoreSslErrors()
+{
+ Q_D(QSslSocket);
+ d->ignoreAllSslErrors = true;
+}
+
+/*!
+ \overload
+ \since 4.6
+
+ This method tells QSslSocket to ignore only the errors given in \a
+ errors.
+
+ Note that you can set the expected certificate in the SSL error:
+ If, for instance, you want to connect to a server that uses
+ a self-signed certificate, consider the following snippet:
+
+ \snippet doc/src/snippets/code/src_network_ssl_qsslsocket.cpp 6
+
+ Multiple calls to this function will replace the list of errors that
+ were passed in previous calls.
+ You can clear the list of errors you want to ignore by calling this
+ function with an empty list.
+
+ \sa sslErrors()
+*/
+void QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
+{
+ Q_D(QSslSocket);
+ d->ignoreErrorsList = errors;
+}
+
+/*!
+ \internal
+*/
+void QSslSocket::connectToHostImplementation(const QString &hostName, quint16 port,
+ OpenMode openMode)
+{
+ Q_D(QSslSocket);
+ if (!d->initialized)
+ d->init();
+ d->initialized = false;
+
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::connectToHostImplementation("
+ << hostName << ',' << port << ',' << openMode << ')';
+#endif
+ if (!d->plainSocket) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "\tcreating internal plain socket";
+#endif
+ d->createPlainSocket(openMode);
+ }
+#ifndef QT_NO_NETWORKPROXY
+ d->plainSocket->setProxy(proxy());
+#endif
+ QIODevice::open(openMode);
+ d->plainSocket->connectToHost(hostName, port, openMode);
+ d->cachedSocketDescriptor = d->plainSocket->socketDescriptor();
+}
+
+/*!
+ \internal
+*/
+void QSslSocket::disconnectFromHostImplementation()
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::disconnectFromHostImplementation()";
+#endif
+ if (!d->plainSocket)
+ return;
+ if (d->state == UnconnectedState)
+ return;
+ if (d->mode == UnencryptedMode && !d->autoStartHandshake) {
+ d->plainSocket->disconnectFromHost();
+ return;
+ }
+ if (d->state <= ConnectingState) {
+ d->pendingClose = true;
+ return;
+ }
+
+ // Perhaps emit closing()
+ if (d->state != ClosingState) {
+ d->state = ClosingState;
+ emit stateChanged(d->state);
+ }
+
+ if (!d->writeBuffer.isEmpty())
+ return;
+
+ if (d->mode == UnencryptedMode) {
+ d->plainSocket->disconnectFromHost();
+ } else {
+ d->disconnectFromHost();
+ }
+}
+
+/*!
+ \reimp
+*/
+qint64 QSslSocket::readData(char *data, qint64 maxlen)
+{
+ Q_D(QSslSocket);
+ qint64 readBytes = 0;
+
+ if (d->mode == UnencryptedMode && !d->autoStartHandshake) {
+ readBytes = d->plainSocket->read(data, maxlen);
+ } else {
+ do {
+ const char *readPtr = d->readBuffer.readPointer();
+ int bytesToRead = qMin<int>(maxlen - readBytes, d->readBuffer.nextDataBlockSize());
+ ::memcpy(data + readBytes, readPtr, bytesToRead);
+ readBytes += bytesToRead;
+ d->readBuffer.free(bytesToRead);
+ } while (!d->readBuffer.isEmpty() && readBytes < maxlen);
+ }
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::readData(" << (void *)data << ',' << maxlen << ") ==" << readBytes;
+#endif
+
+ // possibly trigger another transmit() to decrypt more data from the socket
+ if (d->readBuffer.isEmpty() && d->plainSocket->bytesAvailable())
+ QMetaObject::invokeMethod(this, "_q_flushReadBuffer", Qt::QueuedConnection);
+
+ return readBytes;
+}
+
+/*!
+ \reimp
+*/
+qint64 QSslSocket::writeData(const char *data, qint64 len)
+{
+ Q_D(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::writeData(" << (void *)data << ',' << len << ')';
+#endif
+ if (d->mode == UnencryptedMode && !d->autoStartHandshake)
+ return d->plainSocket->write(data, len);
+
+ char *writePtr = d->writeBuffer.reserve(len);
+ ::memcpy(writePtr, data, len);
+
+ // make sure we flush to the plain socket's buffer
+ QMetaObject::invokeMethod(this, "_q_flushWriteBuffer", Qt::QueuedConnection);
+
+ return len;
+}
+
+/*!
+ \internal
+*/
+QSslSocketPrivate::QSslSocketPrivate()
+ : initialized(false)
+ , mode(QSslSocket::UnencryptedMode)
+ , autoStartHandshake(false)
+ , connectionEncrypted(false)
+ , ignoreAllSslErrors(false)
+ , readyReadEmittedPointer(0)
+ , allowRootCertOnDemandLoading(true)
+ , plainSocket(0)
+{
+ QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
+}
+
+/*!
+ \internal
+*/
+QSslSocketPrivate::~QSslSocketPrivate()
+{
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::init()
+{
+ mode = QSslSocket::UnencryptedMode;
+ autoStartHandshake = false;
+ connectionEncrypted = false;
+ ignoreAllSslErrors = false;
+
+ // we don't want to clear the ignoreErrorsList, so
+ // that it is possible setting it before connecting
+// ignoreErrorsList.clear();
+
+ readBuffer.clear();
+ writeBuffer.clear();
+ configuration.peerCertificate.clear();
+ configuration.peerCertificateChain.clear();
+}
+
+/*!
+ \internal
+*/
+QList<QSslCipher> QSslSocketPrivate::defaultCiphers()
+{
+ QMutexLocker locker(&globalData()->mutex);
+ return globalData()->config->ciphers;
+}
+
+/*!
+ \internal
+*/
+QList<QSslCipher> QSslSocketPrivate::supportedCiphers()
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ return globalData()->supportedCiphers;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setDefaultCiphers(const QList<QSslCipher> &ciphers)
+{
+ QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->config->ciphers = ciphers;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers)
+{
+ QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->supportedCiphers = ciphers;
+}
+
+/*!
+ \internal
+*/
+QList<QSslCertificate> QSslSocketPrivate::defaultCaCertificates()
+{
+ // ### Qt5: rename everything containing "caCertificates" to "rootCertificates" or similar
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ return globalData()->config->caCertificates;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::setDefaultCaCertificates(const QList<QSslCertificate> &certs)
+{
+ QSslSocketPrivate::ensureInitialized();
+ 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;
+}
+
+/*!
+ \internal
+*/
+bool QSslSocketPrivate::addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format,
+ QRegExp::PatternSyntax syntax)
+{
+ QSslSocketPrivate::ensureInitialized();
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(path, format, syntax);
+ if (certs.isEmpty())
+ return false;
+
+ QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->config->caCertificates += certs;
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::addDefaultCaCertificate(const QSslCertificate &cert)
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->config->caCertificates += cert;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::addDefaultCaCertificates(const QList<QSslCertificate> &certs)
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ globalData()->config.detach();
+ globalData()->config->caCertificates += certs;
+}
+
+/*!
+ \internal
+*/
+QSslConfiguration QSslConfigurationPrivate::defaultConfiguration()
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ return QSslConfiguration(globalData()->config.data());
+}
+
+/*!
+ \internal
+*/
+void QSslConfigurationPrivate::setDefaultConfiguration(const QSslConfiguration &configuration)
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ if (globalData()->config == configuration.d)
+ return; // nothing to do
+
+ globalData()->config = const_cast<QSslConfigurationPrivate*>(configuration.d.constData());
+}
+
+/*!
+ \internal
+*/
+void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPrivate *ptr)
+{
+ QSslSocketPrivate::ensureInitialized();
+ QMutexLocker locker(&globalData()->mutex);
+ const QSslConfigurationPrivate *global = globalData()->config.constData();
+
+ if (!global) {
+ ptr = 0;
+ return;
+ }
+
+ ptr->ref = 1;
+ ptr->peerCertificate = global->peerCertificate;
+ ptr->peerCertificateChain = global->peerCertificateChain;
+ ptr->localCertificate = global->localCertificate;
+ ptr->privateKey = global->privateKey;
+ ptr->sessionCipher = global->sessionCipher;
+ ptr->ciphers = global->ciphers;
+ ptr->caCertificates = global->caCertificates;
+ ptr->protocol = global->protocol;
+ ptr->peerVerifyMode = global->peerVerifyMode;
+ ptr->peerVerifyDepth = global->peerVerifyDepth;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::createPlainSocket(QIODevice::OpenMode openMode)
+{
+ Q_Q(QSslSocket);
+ q->setOpenMode(openMode); // <- from QIODevice
+ q->setSocketState(QAbstractSocket::UnconnectedState);
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ q->setLocalPort(0);
+ q->setLocalAddress(QHostAddress());
+ q->setPeerPort(0);
+ q->setPeerAddress(QHostAddress());
+ 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);
+ q->connect(plainSocket, SIGNAL(hostFound()),
+ q, SLOT(_q_hostFoundSlot()),
+ Qt::DirectConnection);
+ q->connect(plainSocket, SIGNAL(disconnected()),
+ q, SLOT(_q_disconnectedSlot()),
+ Qt::DirectConnection);
+ q->connect(plainSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ q, SLOT(_q_stateChangedSlot(QAbstractSocket::SocketState)),
+ Qt::DirectConnection);
+ q->connect(plainSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ q, SLOT(_q_errorSlot(QAbstractSocket::SocketError)),
+ Qt::DirectConnection);
+ q->connect(plainSocket, SIGNAL(readyRead()),
+ q, SLOT(_q_readyReadSlot()),
+ Qt::DirectConnection);
+ q->connect(plainSocket, SIGNAL(bytesWritten(qint64)),
+ q, SLOT(_q_bytesWrittenSlot(qint64)),
+ Qt::DirectConnection);
+#ifndef QT_NO_NETWORKPROXY
+ q->connect(plainSocket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+#endif
+
+ readBuffer.clear();
+ writeBuffer.clear();
+ connectionEncrypted = false;
+ configuration.peerCertificate.clear();
+ configuration.peerCertificateChain.clear();
+ mode = QSslSocket::UnencryptedMode;
+ q->setReadBufferSize(readBufferMaxSize);
+}
+
+void QSslSocketPrivate::pauseSocketNotifiers(QSslSocket *socket)
+{
+ if (!socket->d_func()->plainSocket)
+ return;
+ QAbstractSocketPrivate::pauseSocketNotifiers(socket->d_func()->plainSocket);
+}
+
+void QSslSocketPrivate::resumeSocketNotifiers(QSslSocket *socket)
+{
+ if (!socket->d_func()->plainSocket)
+ return;
+ QAbstractSocketPrivate::resumeSocketNotifiers(socket->d_func()->plainSocket);
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_connectedSlot()
+{
+ Q_Q(QSslSocket);
+ q->setLocalPort(plainSocket->localPort());
+ q->setLocalAddress(plainSocket->localAddress());
+ q->setPeerPort(plainSocket->peerPort());
+ q->setPeerAddress(plainSocket->peerAddress());
+ q->setPeerName(plainSocket->peerName());
+ cachedSocketDescriptor = plainSocket->socketDescriptor();
+
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_connectedSlot()";
+ qDebug() << "\tstate =" << q->state();
+ qDebug() << "\tpeer =" << q->peerName() << q->peerAddress() << q->peerPort();
+ qDebug() << "\tlocal =" << QHostInfo::fromName(q->localAddress().toString()).hostName()
+ << q->localAddress() << q->localPort();
+#endif
+ emit q->connected();
+
+ if (autoStartHandshake) {
+ q->startClientEncryption();
+ } else if (pendingClose) {
+ pendingClose = false;
+ q->disconnectFromHost();
+ }
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_hostFoundSlot()
+{
+ Q_Q(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_hostFoundSlot()";
+ qDebug() << "\tstate =" << q->state();
+#endif
+ emit q->hostFound();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_disconnectedSlot()
+{
+ Q_Q(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_disconnectedSlot()";
+ qDebug() << "\tstate =" << q->state();
+#endif
+ disconnected();
+ emit q->disconnected();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_stateChangedSlot(QAbstractSocket::SocketState state)
+{
+ Q_Q(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_stateChangedSlot(" << state << ')';
+#endif
+ q->setSocketState(state);
+ emit q->stateChanged(state);
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
+{
+ Q_Q(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_errorSlot(" << error << ')';
+ qDebug() << "\tstate =" << q->state();
+ qDebug() << "\terrorString =" << q->errorString();
+#endif
+ q->setSocketError(plainSocket->error());
+ q->setErrorString(plainSocket->errorString());
+ emit q->error(error);
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_readyReadSlot()
+{
+ Q_Q(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_readyReadSlot() -" << plainSocket->bytesAvailable() << "bytes available";
+#endif
+ if (mode == QSslSocket::UnencryptedMode) {
+ if (readyReadEmittedPointer)
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ return;
+ }
+
+ transmit();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_bytesWrittenSlot(qint64 written)
+{
+ Q_Q(QSslSocket);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocket::_q_bytesWrittenSlot(" << written << ')';
+#endif
+
+ if (mode == QSslSocket::UnencryptedMode)
+ emit q->bytesWritten(written);
+ else
+ emit q->encryptedBytesWritten(written);
+ if (state == QAbstractSocket::ClosingState && writeBuffer.isEmpty())
+ q->disconnectFromHost();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_flushWriteBuffer()
+{
+ Q_Q(QSslSocket);
+ if (!writeBuffer.isEmpty())
+ q->flush();
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::_q_flushReadBuffer()
+{
+ // trigger a read from the plainSocket into SSL
+ if (mode != QSslSocket::UnencryptedMode)
+ 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
+#define d d_ptr
+#include "moc_qsslsocket.cpp"
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
new file mode 100644
index 0000000000..648fd8c1d0
--- /dev/null
+++ b/src/network/ssl/qsslsocket.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLSOCKET_H
+#define QSSLSOCKET_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qregexp.h>
+#ifndef QT_NO_OPENSSL
+# include <QtNetwork/qtcpsocket.h>
+# include <QtNetwork/qsslerror.h>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_OPENSSL
+
+class QDir;
+class QSslCipher;
+class QSslCertificate;
+class QSslConfiguration;
+
+class QSslSocketPrivate;
+class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket
+{
+ Q_OBJECT
+public:
+ enum SslMode {
+ UnencryptedMode,
+ SslClientMode,
+ SslServerMode
+ };
+
+ enum PeerVerifyMode {
+ VerifyNone,
+ QueryPeer,
+ VerifyPeer,
+ AutoVerifyPeer
+ };
+
+ QSslSocket(QObject *parent = 0);
+ ~QSslSocket();
+
+ // Autostarting the SSL client handshake.
+ void connectToHostEncrypted(const QString &hostName, quint16 port, OpenMode mode = ReadWrite);
+ void connectToHostEncrypted(const QString &hostName, quint16 port, const QString &sslPeerName, OpenMode mode = ReadWrite);
+ bool setSocketDescriptor(int socketDescriptor, SocketState state = ConnectedState,
+ OpenMode openMode = ReadWrite);
+
+ // ### Qt 5: Make virtual
+ void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value);
+ QVariant socketOption(QAbstractSocket::SocketOption option);
+
+ SslMode mode() const;
+ bool isEncrypted() const;
+
+ QSsl::SslProtocol protocol() const;
+ void setProtocol(QSsl::SslProtocol protocol);
+
+ QSslSocket::PeerVerifyMode peerVerifyMode() const;
+ void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode);
+
+ int peerVerifyDepth() const;
+ void setPeerVerifyDepth(int depth);
+
+ QString peerVerifyName() const;
+ void setPeerVerifyName(const QString &hostName);
+
+ // From QIODevice
+ qint64 bytesAvailable() const;
+ qint64 bytesToWrite() const;
+ bool canReadLine() const;
+ void close();
+ bool atEnd() const;
+ bool flush();
+ void abort();
+
+ // From QAbstractSocket:
+ void setReadBufferSize(qint64 size);
+
+ // Similar to QIODevice's:
+ qint64 encryptedBytesAvailable() const;
+ qint64 encryptedBytesToWrite() const;
+
+ // SSL configuration
+ QSslConfiguration sslConfiguration() const;
+ void setSslConfiguration(const QSslConfiguration &config);
+
+ // Certificate & cipher accessors.
+ void setLocalCertificate(const QSslCertificate &certificate);
+ void setLocalCertificate(const QString &fileName, QSsl::EncodingFormat format = QSsl::Pem);
+ QSslCertificate localCertificate() const;
+ QSslCertificate peerCertificate() const;
+ QList<QSslCertificate> peerCertificateChain() const;
+ QSslCipher sessionCipher() const;
+
+ // Private keys, for server sockets.
+ void setPrivateKey(const QSslKey &key);
+ void setPrivateKey(const QString &fileName, QSsl::KeyAlgorithm algorithm = QSsl::Rsa,
+ QSsl::EncodingFormat format = QSsl::Pem,
+ const QByteArray &passPhrase = QByteArray());
+ QSslKey privateKey() const;
+
+ // Cipher settings.
+ QList<QSslCipher> ciphers() const;
+ void setCiphers(const QList<QSslCipher> &ciphers);
+ void setCiphers(const QString &ciphers);
+ static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
+ static QList<QSslCipher> defaultCiphers();
+ static QList<QSslCipher> supportedCiphers();
+
+ // CA settings.
+ bool addCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ void addCaCertificate(const QSslCertificate &certificate);
+ void addCaCertificates(const QList<QSslCertificate> &certificates);
+ void setCaCertificates(const QList<QSslCertificate> &certificates);
+ QList<QSslCertificate> caCertificates() const;
+ static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format = QSsl::Pem,
+ QRegExp::PatternSyntax syntax = QRegExp::FixedString);
+ static void addDefaultCaCertificate(const QSslCertificate &certificate);
+ static void addDefaultCaCertificates(const QList<QSslCertificate> &certificates);
+ static void setDefaultCaCertificates(const QList<QSslCertificate> &certificates);
+ static QList<QSslCertificate> defaultCaCertificates();
+ static QList<QSslCertificate> systemCaCertificates();
+
+ bool waitForConnected(int msecs = 30000);
+ bool waitForEncrypted(int msecs = 30000);
+ bool waitForReadyRead(int msecs = 30000);
+ bool waitForBytesWritten(int msecs = 30000);
+ bool waitForDisconnected(int msecs = 30000);
+
+ QList<QSslError> sslErrors() const;
+
+ static bool supportsSsl();
+ void ignoreSslErrors(const QList<QSslError> &errors);
+
+public Q_SLOTS:
+ void startClientEncryption();
+ void startServerEncryption();
+ void ignoreSslErrors();
+
+Q_SIGNALS:
+ void encrypted();
+ void peerVerifyError(const QSslError &error);
+ void sslErrors(const QList<QSslError> &errors);
+ void modeChanged(QSslSocket::SslMode newMode);
+ void encryptedBytesWritten(qint64 totalBytes);
+
+protected Q_SLOTS:
+ void connectToHostImplementation(const QString &hostName, quint16 port,
+ OpenMode openMode);
+ void disconnectFromHostImplementation();
+
+protected:
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private:
+ Q_DECLARE_PRIVATE(QSslSocket)
+ Q_DISABLE_COPY(QSslSocket)
+ Q_PRIVATE_SLOT(d_func(), void _q_connectedSlot())
+ Q_PRIVATE_SLOT(d_func(), void _q_hostFoundSlot())
+ Q_PRIVATE_SLOT(d_func(), void _q_disconnectedSlot())
+ Q_PRIVATE_SLOT(d_func(), void _q_stateChangedSlot(QAbstractSocket::SocketState))
+ Q_PRIVATE_SLOT(d_func(), void _q_errorSlot(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_readyReadSlot())
+ Q_PRIVATE_SLOT(d_func(), void _q_bytesWrittenSlot(qint64))
+ Q_PRIVATE_SLOT(d_func(), void _q_flushWriteBuffer())
+ Q_PRIVATE_SLOT(d_func(), void _q_flushReadBuffer())
+ friend class QSslSocketBackendPrivate;
+};
+
+#endif // QT_NO_OPENSSL
+
+QT_END_NAMESPACE
+
+#ifndef QT_NO_OPENSSL
+Q_DECLARE_METATYPE(QList<QSslError>)
+#endif
+
+QT_END_HEADER
+
+#endif
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
new file mode 100644
index 0000000000..78a78a26f6
--- /dev/null
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -0,0 +1,1459 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QSSLSOCKET_DEBUG
+
+#include "qsslsocket_openssl_p.h"
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qsslsocket.h"
+#include "qsslcertificate_p.h"
+#include "qsslcipher_p.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qurl.h>
+#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)
+#define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file
+ PtrSecCertificateGetData QSslSocketPrivate::ptrSecCertificateGetData = 0;
+ PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0;
+ PtrSecTrustCopyAnchorCertificates QSslSocketPrivate::ptrSecTrustCopyAnchorCertificates = 0;
+#elif defined(Q_OS_WIN)
+ PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0;
+ PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0;
+ PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0;
+#elif defined(Q_OS_SYMBIAN)
+#include <e32base.h>
+#include <e32std.h>
+#include <e32debug.h>
+#include <QtCore/private/qcore_symbian_p.h>
+#endif
+
+bool QSslSocketPrivate::s_libraryLoaded = false;
+bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
+bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
+
+/* \internal
+
+ From OpenSSL's thread(3) manual page:
+
+ OpenSSL can safely be used in multi-threaded applications provided that at
+ least two callback functions are set.
+
+ locking_function(int mode, int n, const char *file, int line) is needed to
+ perform locking on shared data structures. (Note that OpenSSL uses a
+ number of global data structures that will be implicitly shared
+ when-whenever ever multiple threads use OpenSSL.) Multi-threaded
+ applications will crash at random if it is not set. ...
+ ...
+ id_function(void) is a function that returns a thread ID. It is not
+ needed on Windows nor on platforms where getpid() returns a different
+ ID for each thread (most notably Linux)
+*/
+class QOpenSslLocks
+{
+public:
+ inline QOpenSslLocks()
+ : initLocker(QMutex::Recursive),
+ locksLocker(QMutex::Recursive)
+ {
+ QMutexLocker locker(&locksLocker);
+ int numLocks = q_CRYPTO_num_locks();
+ locks = new QMutex *[numLocks];
+ memset(locks, 0, numLocks * sizeof(QMutex *));
+ }
+ inline ~QOpenSslLocks()
+ {
+ QMutexLocker locker(&locksLocker);
+ for (int i = 0; i < q_CRYPTO_num_locks(); ++i)
+ delete locks[i];
+ delete [] locks;
+
+ QSslSocketPrivate::deinitialize();
+ }
+ inline QMutex *lock(int num)
+ {
+ QMutexLocker locker(&locksLocker);
+ QMutex *tmp = locks[num];
+ if (!tmp)
+ tmp = locks[num] = new QMutex(QMutex::Recursive);
+ return tmp;
+ }
+
+ QMutex *globalLock()
+ {
+ return &locksLocker;
+ }
+
+ QMutex *initLock()
+ {
+ return &initLocker;
+ }
+
+private:
+ QMutex initLocker;
+ QMutex locksLocker;
+ QMutex **locks;
+};
+Q_GLOBAL_STATIC(QOpenSslLocks, openssl_locks)
+
+extern "C" {
+static void locking_function(int mode, int lockNumber, const char *, int)
+{
+ QMutex *mutex = openssl_locks()->lock(lockNumber);
+
+ // Lock or unlock it
+ if (mode & CRYPTO_LOCK)
+ mutex->lock();
+ else
+ mutex->unlock();
+}
+static unsigned long id_function()
+{
+ return (quintptr)QThread::currentThreadId();
+}
+} // extern "C"
+
+QSslSocketBackendPrivate::QSslSocketBackendPrivate()
+ : ssl(0),
+ ctx(0),
+ pkey(0),
+ readBio(0),
+ writeBio(0),
+ session(0)
+{
+ // Calls SSL_library_init().
+ ensureInitialized();
+}
+
+QSslSocketBackendPrivate::~QSslSocketBackendPrivate()
+{
+}
+
+QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher)
+{
+ QSslCipher ciph;
+
+ char buf [256];
+ QString descriptionOneLine = QString::fromLatin1(q_SSL_CIPHER_description(cipher, buf, sizeof(buf)));
+
+ QStringList descriptionList = descriptionOneLine.split(QLatin1String(" "), QString::SkipEmptyParts);
+ if (descriptionList.size() > 5) {
+ // ### crude code.
+ ciph.d->isNull = false;
+ ciph.d->name = descriptionList.at(0);
+
+ QString protoString = descriptionList.at(1);
+ ciph.d->protocolString = protoString;
+ ciph.d->protocol = QSsl::UnknownProtocol;
+ if (protoString == QLatin1String("SSLv3"))
+ ciph.d->protocol = QSsl::SslV3;
+ else if (protoString == QLatin1String("SSLv2"))
+ ciph.d->protocol = QSsl::SslV2;
+ else if (protoString == QLatin1String("TLSv1"))
+ ciph.d->protocol = QSsl::TlsV1;
+
+ if (descriptionList.at(2).startsWith(QLatin1String("Kx=")))
+ ciph.d->keyExchangeMethod = descriptionList.at(2).mid(3);
+ if (descriptionList.at(3).startsWith(QLatin1String("Au=")))
+ ciph.d->authenticationMethod = descriptionList.at(3).mid(3);
+ if (descriptionList.at(4).startsWith(QLatin1String("Enc=")))
+ ciph.d->encryptionMethod = descriptionList.at(4).mid(4);
+ ciph.d->exportable = (descriptionList.size() > 6 && descriptionList.at(6) == QLatin1String("export"));
+
+ ciph.d->bits = cipher->strength_bits;
+ ciph.d->supportedBits = cipher->alg_bits;
+
+ }
+ return ciph;
+}
+
+// ### This list is shared between all threads, and protected by a
+// mutex. Investigate using thread local storage instead.
+struct QSslErrorList
+{
+ QMutex mutex;
+ QList<QPair<int, int> > errors;
+};
+Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList)
+static int q_X509Callback(int ok, X509_STORE_CTX *ctx)
+{
+ if (!ok) {
+ // Store the error and at which depth the error was detected.
+ _q_sslErrorList()->errors << qMakePair<int, int>(ctx->error, ctx->error_depth);
+ }
+ // Always return OK to allow verification to continue. We're handle the
+ // errors gracefully after collecting all errors, after verification has
+ // completed.
+ return 1;
+}
+
+bool QSslSocketBackendPrivate::initSslContext()
+{
+ Q_Q(QSslSocket);
+
+ // Create and initialize SSL context. Accept SSLv2, SSLv3 and TLSv1.
+ bool client = (mode == QSslSocket::SslClientMode);
+
+ bool reinitialized = false;
+init_context:
+ switch (configuration.protocol) {
+ case QSsl::SslV2:
+ ctx = q_SSL_CTX_new(client ? q_SSLv2_client_method() : q_SSLv2_server_method());
+ break;
+ 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());
+ break;
+ case QSsl::TlsV1:
+ ctx = q_SSL_CTX_new(client ? q_TLSv1_client_method() : q_TLSv1_server_method());
+ break;
+ }
+ if (!ctx) {
+ // After stopping Flash 10 the SSL library looses its ciphers. Try re-adding them
+ // by re-initializing the library.
+ if (!reinitialized) {
+ reinitialized = true;
+ if (q_SSL_library_init() == 1)
+ goto init_context;
+ }
+
+ // ### Bad error code
+ q->setErrorString(QSslSocket::tr("Error creating SSL context (%1)").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ // Enable all bug workarounds.
+ 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;
+ int first = true;
+ QList<QSslCipher> ciphers = configuration.ciphers;
+ if (ciphers.isEmpty())
+ ciphers = defaultCiphers();
+ foreach (const QSslCipher &cipher, ciphers) {
+ if (first)
+ first = false;
+ else
+ cipherString.append(':');
+ cipherString.append(cipher.name().toLatin1());
+ }
+
+ if (!q_SSL_CTX_set_cipher_list(ctx, cipherString.data())) {
+ // ### Bad error code
+ q->setErrorString(QSslSocket::tr("Invalid or empty cipher list (%1)").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ // Add all our CAs to this store.
+ QList<QSslCertificate> expiredCerts;
+ foreach (const QSslCertificate &caCertificate, q->caCertificates()) {
+ // add expired certs later, so that the
+ // valid ones are used before the expired ones
+ if (! caCertificate.isValid()) {
+ expiredCerts.append(caCertificate);
+ } else {
+ q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
+ }
+ }
+
+ bool addExpiredCerts = true;
+#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5)
+ //On Leopard SSL does not work if we add the expired certificates.
+ if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5)
+ addExpiredCerts = false;
+#endif
+ // now add the expired certs
+ if (addExpiredCerts) {
+ foreach (const QSslCertificate &caCertificate, expiredCerts) {
+ q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle());
+ }
+ }
+
+ 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);
+
+ if (!configuration.localCertificate.isNull()) {
+ // Require a private key as well.
+ if (configuration.privateKey.isNull()) {
+ q->setErrorString(QSslSocket::tr("Cannot provide a certificate with no key, %1").arg(getErrorsFromOpenSsl()));
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ // Load certificate
+ if (!q_SSL_CTX_use_certificate(ctx, (X509 *)configuration.localCertificate.handle())) {
+ q->setErrorString(QSslSocket::tr("Error loading local certificate, %1").arg(getErrorsFromOpenSsl()));
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ // Load private key
+ pkey = q_EVP_PKEY_new();
+ // before we were using EVP_PKEY_assign_R* functions and did not use EVP_PKEY_free.
+ // this lead to a memory leak. Now we use the *_set1_* functions which do not
+ // take ownership of the RSA/DSA key instance because the QSslKey already has ownership.
+ if (configuration.privateKey.algorithm() == QSsl::Rsa)
+ q_EVP_PKEY_set1_RSA(pkey, (RSA *)configuration.privateKey.handle());
+ else
+ q_EVP_PKEY_set1_DSA(pkey, (DSA *)configuration.privateKey.handle());
+ if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) {
+ q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(getErrorsFromOpenSsl()));
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ // Check if the certificate matches the private key.
+ if (!q_SSL_CTX_check_private_key(ctx)) {
+ q->setErrorString(QSslSocket::tr("Private key does not certify public key, %1").arg(getErrorsFromOpenSsl()));
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+ }
+
+ // Initialize peer verification.
+ if (configuration.peerVerifyMode == QSslSocket::VerifyNone) {
+ q_SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
+ } else {
+ q_SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, q_X509Callback);
+ }
+
+ // Set verification depth.
+ if (configuration.peerVerifyDepth != 0)
+ q_SSL_CTX_set_verify_depth(ctx, configuration.peerVerifyDepth);
+
+ // Create and initialize SSL session
+ if (!(ssl = q_SSL_new(ctx))) {
+ // ### Bad error code
+ q->setErrorString(QSslSocket::tr("Error creating SSL session, %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ 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 (!q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, ace.constData()))
+ qWarning("could not set SSL_CTRL_SET_TLSEXT_HOSTNAME, Server Name Indication disabled");
+ }
+ }
+#endif
+
+ // Clear the session.
+ q_SSL_clear(ssl);
+ errorList.clear();
+
+ // Initialize memory BIOs for encryption and decryption.
+ readBio = q_BIO_new(q_BIO_s_mem());
+ writeBio = q_BIO_new(q_BIO_s_mem());
+ if (!readBio || !writeBio) {
+ // ### Bad error code
+ q->setErrorString(QSslSocket::tr("Error creating SSL session: %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return false;
+ }
+
+ // Assign the bios.
+ q_SSL_set_bio(ssl, readBio, writeBio);
+
+ if (mode == QSslSocket::SslClientMode)
+ q_SSL_set_connect_state(ssl);
+ else
+ q_SSL_set_accept_state(ssl);
+
+ return true;
+}
+
+/*!
+ \internal
+*/
+void QSslSocketPrivate::deinitialize()
+{
+ q_CRYPTO_set_id_callback(0);
+ q_CRYPTO_set_locking_callback(0);
+}
+
+/*!
+ \internal
+
+ Does the minimum amount of initialization to determine whether SSL
+ is supported or not.
+*/
+
+bool QSslSocketPrivate::supportsSsl()
+{
+ return ensureLibraryLoaded();
+}
+
+bool QSslSocketPrivate::ensureLibraryLoaded()
+{
+ if (!q_resolveOpenSslSymbols())
+ return false;
+
+ // Check if the library itself needs to be initialized.
+ QMutexLocker locker(openssl_locks()->initLock());
+ if (!s_libraryLoaded) {
+ s_libraryLoaded = true;
+
+ // Initialize OpenSSL.
+ q_CRYPTO_set_id_callback(id_function);
+ q_CRYPTO_set_locking_callback(locking_function);
+ if (q_SSL_library_init() != 1)
+ return false;
+ q_SSL_load_error_strings();
+ q_OpenSSL_add_all_algorithms();
+
+ // Initialize OpenSSL's random seed.
+ if (!q_RAND_status()) {
+ struct {
+ int msec;
+ int sec;
+ void *stack;
+ } randomish;
+
+ int attempts = 500;
+ do {
+ if (attempts < 500) {
+#ifdef Q_OS_UNIX
+ struct timespec ts = {0, 33333333};
+ nanosleep(&ts, 0);
+#else
+ Sleep(3);
+#endif
+ randomish.msec = attempts;
+ }
+ randomish.stack = (void *)&randomish;
+ randomish.msec = QTime::currentTime().msec();
+ randomish.sec = QTime::currentTime().second();
+ q_RAND_seed((const char *)&randomish, sizeof(randomish));
+ } while (!q_RAND_status() && --attempts);
+ if (!attempts)
+ return false;
+ }
+ }
+ return true;
+}
+
+void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
+{
+ QMutexLocker locker(openssl_locks()->initLock());
+ if (s_loadedCiphersAndCerts)
+ return;
+ s_loadedCiphersAndCerts = true;
+
+ resetDefaultCiphers();
+
+ //load symbols needed to receive certificates from system store
+#if defined(Q_OS_MAC)
+ QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security");
+ if (securityLib.load()) {
+ ptrSecCertificateGetData = (PtrSecCertificateGetData) securityLib.resolve("SecCertificateGetData");
+ if (!ptrSecCertificateGetData)
+ qWarning("could not resolve symbols in security library"); // should never happen
+
+ ptrSecTrustSettingsCopyCertificates = (PtrSecTrustSettingsCopyCertificates) securityLib.resolve("SecTrustSettingsCopyCertificates");
+ if (!ptrSecTrustSettingsCopyCertificates) { // method was introduced in Leopard, use legacy method if it's not there
+ ptrSecTrustCopyAnchorCertificates = (PtrSecTrustCopyAnchorCertificates) securityLib.resolve("SecTrustCopyAnchorCertificates");
+ if (!ptrSecTrustCopyAnchorCertificates)
+ qWarning("could not resolve symbols in security library"); // should never happen
+ }
+ } else {
+ qWarning("could not load security library");
+ }
+#elif defined(Q_OS_WIN)
+ HINSTANCE hLib = LoadLibraryW(L"Crypt32");
+ if (hLib) {
+#if defined(Q_OS_WINCE)
+ ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, L"CertOpenStore");
+ ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, L"CertFindCertificateInStore");
+ ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, L"CertCloseStore");
+#else
+ ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW");
+ ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore");
+ ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore");
+#endif
+ if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore)
+ qWarning("could not resolve symbols in crypt32 library"); // should never happen
+ } 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
+ // if on-demand loading was not enabled, load the certs now
+ if (!s_loadRootCertsOnDemand)
+ setDefaultCaCertificates(systemCaCertificates());
+}
+
+/*!
+ \internal
+
+ Declared static in QSslSocketPrivate, makes sure the SSL libraries have
+ been initialized.
+*/
+
+void QSslSocketPrivate::ensureInitialized()
+{
+ if (!supportsSsl())
+ return;
+
+ ensureCiphersAndCertsLoaded();
+}
+
+/*!
+ \internal
+
+ Declared static in QSslSocketPrivate, backend-dependent loading of
+ application-wide global ciphers.
+*/
+void QSslSocketPrivate::resetDefaultCiphers()
+{
+ SSL_CTX *myCtx = q_SSL_CTX_new(q_SSLv23_client_method());
+ SSL *mySsl = q_SSL_new(myCtx);
+
+ QList<QSslCipher> ciphers;
+
+ STACK_OF(SSL_CIPHER) *supportedCiphers = q_SSL_get_ciphers(mySsl);
+ for (int i = 0; i < q_sk_SSL_CIPHER_num(supportedCiphers); ++i) {
+ if (SSL_CIPHER *cipher = q_sk_SSL_CIPHER_value(supportedCiphers, i)) {
+ if (cipher->valid) {
+ QSslCipher ciph = QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(cipher);
+ if (!ciph.isNull()) {
+ if (!ciph.name().toLower().startsWith(QLatin1String("adh")))
+ ciphers << ciph;
+ }
+ }
+ }
+ }
+
+ q_SSL_CTX_free(myCtx);
+ q_SSL_free(mySsl);
+
+ setDefaultSupportedCiphers(ciphers);
+ setDefaultCiphers(ciphers);
+}
+
+#if defined(Q_OS_SYMBIAN)
+
+CSymbianCertificateRetriever::CSymbianCertificateRetriever() : CActive(CActive::EPriorityStandard),
+ iCertificatePtr(0,0,0), iSequenceError(KErrNone)
+{
+}
+
+CSymbianCertificateRetriever::~CSymbianCertificateRetriever()
+{
+ iThread.Close();
+}
+
+CSymbianCertificateRetriever* CSymbianCertificateRetriever::NewL()
+{
+ CSymbianCertificateRetriever* self = new (ELeave) CSymbianCertificateRetriever();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+}
+
+int CSymbianCertificateRetriever::GetCertificates(QList<QByteArray> &certificates)
+{
+ iCertificates = &certificates;
+
+ TRequestStatus status;
+ iThread.Logon(status);
+ iThread.Resume();
+ User::WaitForRequest(status);
+ if (iThread.ExitType() == EExitKill)
+ return KErrDied;
+ else
+ return status.Int(); // Logon() completes with the thread's exit value
+}
+
+void CSymbianCertificateRetriever::doThreadEntryL()
+{
+ CActiveScheduler* activeScheduler = new (ELeave) CActiveScheduler;
+ CleanupStack::PushL(activeScheduler);
+ CActiveScheduler::Install(activeScheduler);
+
+ CActiveScheduler::Add(this);
+
+ // These aren't deleted in the destructor so leaving the to CS is ok
+ iCertStore = CUnifiedCertStore::NewLC(qt_s60GetRFs(), EFalse);
+ iCertFilter = CCertAttributeFilter::NewLC();
+
+ // only interested in CA certs
+ iCertFilter->SetOwnerType(ECACertificate);
+ // only interested in X.509 format (we don't support WAP formats)
+ iCertFilter->SetFormat(EX509Certificate);
+
+ // Kick off the sequence by initializing the cert store
+ iState = Initializing;
+ iCertStore->Initialize(iStatus);
+ SetActive();
+
+ CActiveScheduler::Start();
+
+ // Sequence complete, clean up
+
+ // These MUST be cleaned up before the installed CActiveScheduler is destroyed and can't be left to the
+ // destructor of CSymbianCertificateRetriever. Otherwise the destructor of CActiveScheduler will get
+ // stuck.
+ iCertInfos.Close();
+ CleanupStack::PopAndDestroy(3); // activeScheduler, iCertStore, iCertFilter
+}
+
+
+TInt CSymbianCertificateRetriever::ThreadEntryPoint(TAny* aParams)
+{
+ User::SetCritical(User::EProcessCritical);
+ CTrapCleanup* cleanupStack = CTrapCleanup::New();
+
+ CSymbianCertificateRetriever* self = (CSymbianCertificateRetriever*) aParams;
+ TRAPD(err, self->doThreadEntryL());
+ delete cleanupStack;
+
+ // doThreadEntryL() can leave only before the retrieval sequence is started
+ if (err)
+ return err;
+ else
+ return self->iSequenceError; // return any error that occurred during the retrieval
+}
+
+void CSymbianCertificateRetriever::ConstructL()
+{
+ TInt err;
+ int i=0;
+ QString name(QLatin1String("CertWorkerThread-%1"));
+ //recently closed thread names remain in use for a while until all handles have been closed
+ //including users of RUndertaker
+ do {
+ err = iThread.Create(qt_QString2TPtrC(name.arg(i++)),
+ CSymbianCertificateRetriever::ThreadEntryPoint, 16384, NULL, this);
+ } while (err == KErrAlreadyExists);
+ User::LeaveIfError(err);
+}
+
+void CSymbianCertificateRetriever::DoCancel()
+{
+ switch(iState) {
+ case Initializing:
+ iCertStore->CancelInitialize();
+ break;
+ case Listing:
+ iCertStore->CancelList();
+ break;
+ case RetrievingCertificates:
+ iCertStore->CancelGetCert();
+ break;
+ }
+}
+
+TInt CSymbianCertificateRetriever::RunError(TInt aError)
+{
+ // If something goes wrong in the sequence, abort the sequence
+ iSequenceError = aError; // this gets reported to the client in the TRequestStatus
+ CActiveScheduler::Stop();
+ return KErrNone;
+}
+
+void CSymbianCertificateRetriever::GetCertificateL()
+{
+ if (iCurrentCertIndex < iCertInfos.Count()) {
+ CCTCertInfo* certInfo = iCertInfos[iCurrentCertIndex++];
+ iCertificateData = QByteArray();
+ QT_TRYCATCH_LEAVING(iCertificateData.resize(certInfo->Size()));
+ iCertificatePtr.Set((TUint8*)iCertificateData.data(), 0, iCertificateData.size());
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "getting " << qt_TDesC2QString(certInfo->Label()) << " size=" << certInfo->Size();
+ qDebug() << "format=" << certInfo->CertificateFormat();
+ qDebug() << "ownertype=" << certInfo->CertificateOwnerType();
+ qDebug() << "type=" << hex << certInfo->Type().iUid;
+#endif
+ iCertStore->Retrieve(*certInfo, iCertificatePtr, iStatus);
+ iState = RetrievingCertificates;
+ SetActive();
+ } else {
+ //reached end of list
+ CActiveScheduler::Stop();
+ }
+}
+
+void CSymbianCertificateRetriever::RunL()
+{
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "CSymbianCertificateRetriever::RunL status " << iStatus.Int() << " count " << iCertInfos.Count() << " index " << iCurrentCertIndex;
+#endif
+ switch (iState) {
+ case Initializing:
+ User::LeaveIfError(iStatus.Int()); // initialise fail means pointless to continue
+ iState = Listing;
+ iCertStore->List(iCertInfos, *iCertFilter, iStatus);
+ SetActive();
+ break;
+
+ case Listing:
+ User::LeaveIfError(iStatus.Int()); // listing fail means pointless to continue
+ iCurrentCertIndex = 0;
+ GetCertificateL();
+ break;
+
+ case RetrievingCertificates:
+ if (iStatus.Int() == KErrNone)
+ iCertificates->append(iCertificateData);
+ else
+ qWarning() << "CSymbianCertificateRetriever: failed to retrieve a certificate, error " << iStatus.Int();
+ GetCertificateL();
+ break;
+ }
+}
+#endif // defined(Q_OS_SYMBIAN)
+
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
+{
+ ensureInitialized();
+#ifdef QSSLSOCKET_DEBUG
+ QElapsedTimer timer;
+ timer.start();
+#endif
+ QList<QSslCertificate> systemCerts;
+#if defined(Q_OS_MAC)
+ CFArrayRef cfCerts;
+ OSStatus status = 1;
+
+ OSStatus SecCertificateGetData (
+ SecCertificateRef certificate,
+ CSSM_DATA_PTR data
+ );
+
+ if (ptrSecCertificateGetData) {
+ if (ptrSecTrustSettingsCopyCertificates)
+ status = ptrSecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
+ else if (ptrSecTrustCopyAnchorCertificates)
+ status = ptrSecTrustCopyAnchorCertificates(&cfCerts);
+ if (!status) {
+ CFIndex size = CFArrayGetCount(cfCerts);
+ for (CFIndex i = 0; i < size; ++i) {
+ SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
+ CSSM_DATA data;
+ CSSM_DATA_PTR dataPtr = &data;
+ if (ptrSecCertificateGetData(cfCert, dataPtr)) {
+ qWarning("error retrieving a CA certificate from the system store");
+ } else {
+ int len = data.Length;
+ char *rawData = reinterpret_cast<char *>(data.Data);
+ QByteArray rawCert(rawData, len);
+ systemCerts.append(QSslCertificate::fromData(rawCert, QSsl::Der));
+ }
+ }
+ CFRelease(cfCerts);
+ }
+ else {
+ // no detailed error handling here
+ qWarning("could not retrieve system CA certificates");
+ }
+ }
+#elif defined(Q_OS_WIN)
+ if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
+ HCERTSTORE hSystemStore;
+#if defined(Q_OS_WINCE)
+ hSystemStore = ptrCertOpenSystemStoreW(CERT_STORE_PROV_SYSTEM_W,
+ 0,
+ 0,
+ CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER,
+ L"ROOT");
+#else
+ hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT");
+#endif
+ if(hSystemStore) {
+ PCCERT_CONTEXT pc = NULL;
+ while(1) {
+ pc = ptrCertFindCertificateInStore( hSystemStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pc);
+ if(!pc)
+ break;
+ QByteArray der((const char *)(pc->pbCertEncoded), static_cast<int>(pc->cbCertEncoded));
+ QSslCertificate cert(der, QSsl::Der);
+ systemCerts.append(cert);
+ }
+ ptrCertCloseStore(hSystemStore, 0);
+ }
+ }
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN)
+ QSet<QString> certFiles;
+ QList<QByteArray> directories = unixRootCertDirectories();
+ QDir currentDir;
+ QStringList nameFilters;
+ nameFilters << QLatin1String("*.pem") << QLatin1String("*.crt");
+ currentDir.setNameFilters(nameFilters);
+ for (int a = 0; a < directories.count(); a++) {
+ currentDir.setPath(QLatin1String(directories.at(a)));
+ QDirIterator it(currentDir);
+ while(it.hasNext()) {
+ it.next();
+ // use canonical path here to not load the same certificate twice if symlinked
+ certFiles.insert(it.fileInfo().canonicalFilePath());
+ }
+ }
+ QSetIterator<QString> it(certFiles);
+ while(it.hasNext()) {
+ systemCerts.append(QSslCertificate::fromPath(it.next()));
+ }
+ systemCerts.append(QSslCertificate::fromPath(QLatin1String("/etc/pki/tls/certs/ca-bundle.crt"), QSsl::Pem)); // Fedora, Mandriva
+ systemCerts.append(QSslCertificate::fromPath(QLatin1String("/usr/local/share/certs/ca-root-nss.crt"), QSsl::Pem)); // FreeBSD's ca_root_nss
+
+#elif defined(Q_OS_SYMBIAN)
+ QList<QByteArray> certs;
+ QScopedPointer<CSymbianCertificateRetriever> retriever(CSymbianCertificateRetriever::NewL());
+
+ retriever->GetCertificates(certs);
+ foreach (const QByteArray &encodedCert, certs) {
+ QSslCertificate cert(encodedCert, QSsl::Der);
+ if (!cert.isNull()) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "imported certificate: " << cert.issuerInfo(QSslCertificate::CommonName);
+#endif
+ systemCerts.append(cert);
+ }
+ }
+#endif
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "systemCaCertificates retrieval time " << timer.elapsed() << "ms";
+ qDebug() << "imported " << systemCerts.count() << " certificates";
+#endif
+
+ return systemCerts;
+}
+
+void QSslSocketBackendPrivate::startClientEncryption()
+{
+ if (!initSslContext()) {
+ // ### report error: internal OpenSSL failure
+ return;
+ }
+
+ // Start connecting. This will place outgoing data in the BIO, so we
+ // follow up with calling transmit().
+ startHandshake();
+ transmit();
+}
+
+void QSslSocketBackendPrivate::startServerEncryption()
+{
+ if (!initSslContext()) {
+ // ### report error: internal OpenSSL failure
+ return;
+ }
+
+ // Start connecting. This will place outgoing data in the BIO, so we
+ // follow up with calling transmit().
+ startHandshake();
+ transmit();
+}
+
+/*!
+ \internal
+
+ Transmits encrypted data between the BIOs and the socket.
+*/
+void QSslSocketBackendPrivate::transmit()
+{
+ Q_Q(QSslSocket);
+
+ // If we don't have any SSL context, don't bother transmitting.
+ if (!ssl)
+ return;
+
+ bool transmitting;
+ do {
+ transmitting = false;
+
+ // If the connection is secure, we can transfer data from the write
+ // buffer (in plain text) to the write BIO through SSL_write.
+ if (connectionEncrypted && !writeBuffer.isEmpty()) {
+ qint64 totalBytesWritten = 0;
+ int nextDataBlockSize;
+ while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) {
+ int writtenBytes = q_SSL_write(ssl, writeBuffer.readPointer(), nextDataBlockSize);
+ if (writtenBytes <= 0) {
+ // ### Better error handling.
+ q->setErrorString(QSslSocket::tr("Unable to write data: %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return;
+ }
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: encrypted" << writtenBytes << "bytes";
+#endif
+ writeBuffer.free(writtenBytes);
+ totalBytesWritten += writtenBytes;
+
+ if (writtenBytes < nextDataBlockSize) {
+ // break out of the writing loop and try again after we had read
+ transmitting = true;
+ break;
+ }
+ }
+
+ if (totalBytesWritten > 0) {
+ // Don't emit bytesWritten() recursively.
+ if (!emittedBytesWritten) {
+ emittedBytesWritten = true;
+ emit q->bytesWritten(totalBytesWritten);
+ emittedBytesWritten = false;
+ }
+ }
+ }
+
+ // Check if we've got any data to be written to the socket.
+ QVarLengthArray<char, 4096> data;
+ int pendingBytes;
+ while (plainSocket->isValid() && (pendingBytes = q_BIO_pending(writeBio)) > 0) {
+ // Read encrypted data from the write BIO into a buffer.
+ data.resize(pendingBytes);
+ int encryptedBytesRead = q_BIO_read(writeBio, data.data(), pendingBytes);
+
+ // Write encrypted data from the buffer to the socket.
+ plainSocket->write(data.constData(), encryptedBytesRead);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: wrote" << encryptedBytesRead << "encrypted bytes to the socket";
+#endif
+ transmitting = true;
+ }
+
+ // Check if we've got any data to be read from the socket.
+ if (!connectionEncrypted || !readBufferMaxSize || readBuffer.size() < readBufferMaxSize)
+ while ((pendingBytes = plainSocket->bytesAvailable()) > 0) {
+ // Read encrypted data from the socket into a buffer.
+ data.resize(pendingBytes);
+ // just peek() here because q_BIO_write could write less data than expected
+ int encryptedBytesRead = plainSocket->peek(data.data(), pendingBytes);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: read" << encryptedBytesRead << "encrypted bytes from the socket";
+#endif
+ // Write encrypted data from the buffer into the read BIO.
+ int writtenToBio = q_BIO_write(readBio, data.constData(), encryptedBytesRead);
+
+ // do the actual read() here and throw away the results.
+ if (writtenToBio > 0) {
+ // ### TODO: make this cheaper by not making it memcpy. E.g. make it work with data=0x0 or make it work with seek
+ plainSocket->read(data.data(), writtenToBio);
+ } else {
+ // ### Better error handling.
+ q->setErrorString(QSslSocket::tr("Unable to decrypt data: %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return;
+ }
+
+ transmitting = true;
+ }
+
+ // If the connection isn't secured yet, this is the time to retry the
+ // connect / accept.
+ if (!connectionEncrypted) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: testing encryption";
+#endif
+ if (startHandshake()) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: encryption established";
+#endif
+ connectionEncrypted = true;
+ transmitting = true;
+ } else if (plainSocket->state() != QAbstractSocket::ConnectedState) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: connection lost";
+#endif
+ break;
+ } else {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: encryption not done yet";
+#endif
+ }
+ }
+
+ // If the request is small and the remote host closes the transmission
+ // after sending, there's a chance that startHandshake() will already
+ // have triggered a shutdown.
+ if (!ssl)
+ continue;
+
+ // We always read everything from the SSL decryption buffers, even if
+ // we have a readBufferMaxSize. There's no point in leaving data there
+ // just so that readBuffer.size() == readBufferMaxSize.
+ int readBytes = 0;
+ data.resize(4096);
+ ::memset(data.data(), 0, data.size());
+ do {
+ // Don't use SSL_pending(). It's very unreliable.
+ if ((readBytes = q_SSL_read(ssl, data.data(), data.size())) > 0) {
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: decrypted" << readBytes << "bytes";
+#endif
+ char *ptr = readBuffer.reserve(readBytes);
+ ::memcpy(ptr, data.data(), readBytes);
+
+ if (readyReadEmittedPointer)
+ *readyReadEmittedPointer = true;
+ emit q->readyRead();
+ transmitting = true;
+ continue;
+ }
+
+ // Error.
+ switch (q_SSL_get_error(ssl, readBytes)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // Out of data.
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ // The remote host closed the connection.
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::transmit: remote disconnect";
+#endif
+ plainSocket->disconnectFromHost();
+ break;
+ case SSL_ERROR_SYSCALL: // some IO error
+ case SSL_ERROR_SSL: // error in the SSL library
+ // we do not know exactly what the error is, nor whether we can recover from it,
+ // so just return to prevent an endless loop in the outer "while" statement
+ q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ return;
+ default:
+ // SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: can only happen with a
+ // BIO_s_connect() or BIO_s_accept(), which we do not call.
+ // SSL_ERROR_WANT_X509_LOOKUP: can only happen with a
+ // SSL_CTX_set_client_cert_cb(), which we do not call.
+ // So this default case should never be triggered.
+ q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::UnknownSocketError);
+ emit q->error(QAbstractSocket::UnknownSocketError);
+ break;
+ }
+ } while (ssl && readBytes > 0);
+ } while (ssl && ctx && transmitting);
+}
+
+static QSslError _q_OpenSSL_to_QSslError(int errorCode, const QSslCertificate &cert)
+{
+ QSslError error;
+ switch (errorCode) {
+ case X509_V_OK:
+ // X509_V_OK is also reported if the peer had no certificate.
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ error = QSslError(QSslError::UnableToGetIssuerCertificate, cert); break;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ error = QSslError(QSslError::UnableToDecryptCertificateSignature, cert); break;
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ error = QSslError(QSslError::UnableToDecodeIssuerPublicKey, cert); break;
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ error = QSslError(QSslError::CertificateSignatureFailed, cert); break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ error = QSslError(QSslError::CertificateNotYetValid, cert); break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ error = QSslError(QSslError::CertificateExpired, cert); break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ error = QSslError(QSslError::InvalidNotBeforeField, cert); break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ error = QSslError(QSslError::InvalidNotAfterField, cert); break;
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ error = QSslError(QSslError::SelfSignedCertificate, cert); break;
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ error = QSslError(QSslError::SelfSignedCertificateInChain, cert); break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ error = QSslError(QSslError::UnableToGetLocalIssuerCertificate, cert); break;
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ error = QSslError(QSslError::UnableToVerifyFirstCertificate, cert); break;
+ case X509_V_ERR_CERT_REVOKED:
+ error = QSslError(QSslError::CertificateRevoked, cert); break;
+ case X509_V_ERR_INVALID_CA:
+ error = QSslError(QSslError::InvalidCaCertificate, cert); break;
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ error = QSslError(QSslError::PathLengthExceeded, cert); break;
+ case X509_V_ERR_INVALID_PURPOSE:
+ error = QSslError(QSslError::InvalidPurpose, cert); break;
+ case X509_V_ERR_CERT_UNTRUSTED:
+ error = QSslError(QSslError::CertificateUntrusted, cert); break;
+ case X509_V_ERR_CERT_REJECTED:
+ error = QSslError(QSslError::CertificateRejected, cert); break;
+ default:
+ error = QSslError(QSslError::UnspecifiedError, cert); break;
+ }
+ return error;
+}
+
+bool QSslSocketBackendPrivate::startHandshake()
+{
+ Q_Q(QSslSocket);
+
+ // Check if the connection has been established. Get all errors from the
+ // verification stage.
+ _q_sslErrorList()->mutex.lock();
+ _q_sslErrorList()->errors.clear();
+ int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
+
+ const QList<QPair<int, int> > &lastErrors = _q_sslErrorList()->errors;
+ for (int i = 0; i < lastErrors.size(); ++i) {
+ const QPair<int, int> &currentError = lastErrors.at(i);
+ // Initialize the peer certificate chain in order to find which certificate caused this error
+ if (configuration.peerCertificateChain.isEmpty())
+ configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl));
+ emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.first,
+ configuration.peerCertificateChain.value(currentError.second)));
+ if (q->state() != QAbstractSocket::ConnectedState)
+ break;
+ }
+
+ errorList << lastErrors;
+ _q_sslErrorList()->mutex.unlock();
+
+ // Connection aborted during handshake phase.
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+
+ // Check if we're encrypted or not.
+ if (result <= 0) {
+ switch (q_SSL_get_error(ssl, result)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ // The handshake is not yet complete.
+ break;
+ default:
+ q->setErrorString(QSslSocket::tr("Error during SSL handshake: %1").arg(getErrorsFromOpenSsl()));
+ q->setSocketError(QAbstractSocket::SslHandshakeFailedError);
+#ifdef QSSLSOCKET_DEBUG
+ qDebug() << "QSslSocketBackendPrivate::startHandshake: error!" << q->errorString();
+#endif
+ emit q->error(QAbstractSocket::SslHandshakeFailedError);
+ q->abort();
+ }
+ return false;
+ }
+
+ // Store the peer certificate and chain. For clients, the peer certificate
+ // chain includes the peer certificate; for servers, it doesn't. Both the
+ // peer certificate and the chain may be empty if the peer didn't present
+ // any certificate.
+ if (configuration.peerCertificateChain.isEmpty())
+ configuration.peerCertificateChain = STACKOFX509_to_QSslCertificates(q_SSL_get_peer_cert_chain(ssl));
+ X509 *x509 = q_SSL_get_peer_certificate(ssl);
+ configuration.peerCertificate = QSslCertificatePrivate::QSslCertificate_from_X509(x509);
+ q_X509_free(x509);
+
+ // Start translating errors.
+ QList<QSslError> errors;
+
+ if (QSslCertificatePrivate::isBlacklisted(configuration.peerCertificate)) {
+ QSslError error(QSslError::CertificateBlacklisted, configuration.peerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+
+ bool doVerifyPeer = configuration.peerVerifyMode == QSslSocket::VerifyPeer
+ || (configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer
+ && mode == QSslSocket::SslClientMode);
+
+ // Check the peer certificate itself. First try the subject's common name
+ // (CN) as a wildcard, then try all alternate subject name DNS entries the
+ // same way.
+ if (!configuration.peerCertificate.isNull()) {
+ // but only if we're a client connecting to a server
+ // if we're the server, don't check CN
+ if (mode == QSslSocket::SslClientMode) {
+ QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName);
+ QString commonName = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName);
+
+ if (!isMatchingHostname(commonName.toLower(), peerName.toLower())) {
+ bool matched = false;
+ foreach (const QString &altName, configuration.peerCertificate
+ .alternateSubjectNames().values(QSsl::DnsEntry)) {
+ if (isMatchingHostname(altName.toLower(), peerName.toLower())) {
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ // No matches in common names or alternate names.
+ QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+ }
+ } else {
+ // No peer certificate presented. Report as error if the socket
+ // expected one.
+ if (doVerifyPeer) {
+ QSslError error(QSslError::NoPeerCertificate);
+ errors << error;
+ emit q->peerVerifyError(error);
+ if (q->state() != QAbstractSocket::ConnectedState)
+ return false;
+ }
+ }
+
+ // Translate errors from the error list into QSslErrors.
+ for (int i = 0; i < errorList.size(); ++i) {
+ const QPair<int, int> &errorAndDepth = errorList.at(i);
+ int err = errorAndDepth.first;
+ int depth = errorAndDepth.second;
+ errors << _q_OpenSSL_to_QSslError(err, configuration.peerCertificateChain.value(depth));
+ }
+
+ if (!errors.isEmpty()) {
+ sslErrors = errors;
+ emit q->sslErrors(errors);
+
+ bool doEmitSslError;
+ if (!ignoreErrorsList.empty()) {
+ // check whether the errors we got are all in the list of expected errors
+ // (applies only if the method QSslSocket::ignoreSslErrors(const QList<QSslError> &errors)
+ // was called)
+ doEmitSslError = false;
+ for (int a = 0; a < errors.count(); a++) {
+ if (!ignoreErrorsList.contains(errors.at(a))) {
+ doEmitSslError = true;
+ break;
+ }
+ }
+ } else {
+ // if QSslSocket::ignoreSslErrors(const QList<QSslError> &errors) was not called and
+ // we get an SSL error, emit a signal unless we ignored all errors (by calling
+ // QSslSocket::ignoreSslErrors() )
+ doEmitSslError = !ignoreAllSslErrors;
+ }
+ // check whether we need to emit an SSL handshake error
+ if (doVerifyPeer && doEmitSslError) {
+ q->setErrorString(sslErrors.first().errorString());
+ q->setSocketError(QAbstractSocket::SslHandshakeFailedError);
+ emit q->error(QAbstractSocket::SslHandshakeFailedError);
+ plainSocket->disconnectFromHost();
+ return false;
+ }
+ } else {
+ sslErrors.clear();
+ }
+
+ // if we have a max read buffer size, reset the plain socket's to 1k
+ if (readBufferMaxSize)
+ plainSocket->setReadBufferSize(1024);
+
+ connectionEncrypted = true;
+ emit q->encrypted();
+ if (autoStartHandshake && pendingClose) {
+ pendingClose = false;
+ q->disconnectFromHost();
+ }
+ return true;
+}
+
+void QSslSocketBackendPrivate::disconnectFromHost()
+{
+ if (ssl) {
+ q_SSL_shutdown(ssl);
+ transmit();
+ }
+ plainSocket->disconnectFromHost();
+}
+
+void QSslSocketBackendPrivate::disconnected()
+{
+ if (ssl) {
+ q_SSL_free(ssl);
+ ssl = 0;
+ }
+ if (ctx) {
+ q_SSL_CTX_free(ctx);
+ ctx = 0;
+ }
+ if (pkey) {
+ q_EVP_PKEY_free(pkey);
+ pkey = 0;
+ }
+
+}
+
+QSslCipher QSslSocketBackendPrivate::sessionCipher() const
+{
+ if (!ssl || !ctx)
+ return QSslCipher();
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ // FIXME This is fairly evil, but needed to keep source level compatibility
+ // with the OpenSSL 0.9.x implementation at maximum -- some other functions
+ // don't take a const SSL_CIPHER* when they should
+ SSL_CIPHER *sessionCipher = const_cast<SSL_CIPHER *>(q_SSL_get_current_cipher(ssl));
+#else
+ SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl);
+#endif
+ return sessionCipher ? QSslCipher_from_SSL_CIPHER(sessionCipher) : QSslCipher();
+}
+
+QList<QSslCertificate> QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509)
+{
+ ensureInitialized();
+ QList<QSslCertificate> certificates;
+ for (int i = 0; i < q_sk_X509_num(x509); ++i) {
+ if (X509 *entry = q_sk_X509_value(x509, i))
+ certificates << QSslCertificatePrivate::QSslCertificate_from_X509(entry);
+ }
+ return certificates;
+}
+
+QString QSslSocketBackendPrivate::getErrorsFromOpenSsl()
+{
+ QString errorString;
+ unsigned long errNum;
+ while((errNum = q_ERR_get_error())) {
+ if (! errorString.isEmpty())
+ errorString.append(QLatin1String(", "));
+ const char *error = q_ERR_error_string(errNum, NULL);
+ errorString.append(QString::fromAscii(error)); // error is ascii according to man ERR_error_string
+ }
+ return errorString;
+}
+
+bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname)
+{
+ int wildcard = cn.indexOf(QLatin1Char('*'));
+
+ // Check this is a wildcard cert, if not then just compare the strings
+ if (wildcard < 0)
+ return cn == hostname;
+
+ int firstCnDot = cn.indexOf(QLatin1Char('.'));
+ int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1);
+
+ // Check at least 3 components
+ if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
+ return false;
+
+ // Check * is last character of 1st component (ie. there's a following .)
+ if (wildcard+1 != firstCnDot)
+ return false;
+
+ // Check only one star
+ if (cn.lastIndexOf(QLatin1Char('*')) != wildcard)
+ return false;
+
+ // Check characters preceding * (if any) match
+ if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard)))
+ return false;
+
+ // Check characters following first . match
+ if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot))
+ return false;
+
+ // Check if the hostname is an IP address, if so then wildcards are not allowed
+ QHostAddress addr(hostname);
+ if (!addr.isNull())
+ return false;
+
+ // Ok, I guess this was a wildcard CN and the hostname matches.
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
new file mode 100644
index 0000000000..ca49fabc13
--- /dev/null
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLSOCKET_OPENSSL_P_H
+#define QSSLSOCKET_OPENSSL_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 "qsslsocket_p.h"
+
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#if defined(OCSP_RESPONSE)
+#undef OCSP_RESPONSE
+#endif
+#endif
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#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;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QSslSocketBackendPrivate : public QSslSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QSslSocket)
+public:
+ QSslSocketBackendPrivate();
+ virtual ~QSslSocketBackendPrivate();
+
+ // SSL context
+ bool initSslContext();
+ SSL *ssl;
+ SSL_CTX *ctx;
+ EVP_PKEY *pkey;
+ BIO *readBio;
+ BIO *writeBio;
+ SSL_SESSION *session;
+ X509_STORE *certificateStore;
+ X509_STORE_CTX *certificateStoreCtx;
+ QList<QPair<int, int> > errorList;
+
+ // Platform specific functions
+ void startClientEncryption();
+ void startServerEncryption();
+ void transmit();
+ bool startHandshake();
+ void disconnectFromHost();
+ void disconnected();
+ QSslCipher sessionCipher() const;
+
+ static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher);
+ static QList<QSslCertificate> STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509);
+ Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
+ static QString getErrorsFromOpenSsl();
+};
+
+#if defined(Q_OS_SYMBIAN)
+
+#include <QByteArray>
+#include <e32base.h>
+#include <f32file.h>
+#include <unifiedcertstore.h> // link against certstore.lib
+#include <ccertattributefilter.h> // link against ctframework.lib
+
+// The purpose of this class is to wrap the asynchronous API of Symbian certificate store to one
+// synchronizable call. The user of this class needs to provide a TRequestStatus object which can
+// be used with User::WaitForRequest() unlike with the calls of the certificate store API.
+// A thread is used instead of a CActiveSchedulerWait scheme, because that would make the call
+// asynchronous (other events might be processed during the call even though the call would be seemingly
+// synchronous).
+
+class CSymbianCertificateRetriever : public CActive
+{
+public:
+ static CSymbianCertificateRetriever* NewL();
+ ~CSymbianCertificateRetriever();
+
+ int GetCertificates(QList<QByteArray> &aCertificates);
+
+private:
+ void ConstructL();
+ CSymbianCertificateRetriever();
+ static TInt ThreadEntryPoint(TAny* aParams);
+ void doThreadEntryL();
+ void GetCertificateL();
+ void DoCancel();
+ void RunL();
+ TInt RunError(TInt aError);
+
+private:
+ enum {
+ Initializing,
+ Listing,
+ RetrievingCertificates
+ } iState;
+
+ RThread iThread;
+ CUnifiedCertStore* iCertStore;
+ RMPointerArray<CCTCertInfo> iCertInfos;
+ CCertAttributeFilter* iCertFilter;
+ TInt iCurrentCertIndex;
+ QByteArray iCertificateData;
+ TPtr8 iCertificatePtr;
+ QList<QByteArray>* iCertificates;
+ TInt iSequenceError;
+};
+
+
+#endif
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
new file mode 100644
index 0000000000..b1310ccb8d
--- /dev/null
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -0,0 +1,888 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsslsocket_openssl_symbols_p.h"
+
+#ifdef Q_OS_WIN
+# include <private/qsystemlibrary_p.h>
+#else
+# include <QtCore/qlibrary.h>
+#endif
+#include <QtCore/qmutex.h>
+#include <private/qmutexpool_p.h>
+#include <QtCore/qdatetime.h>
+#if defined(Q_OS_UNIX)
+#include <QtCore/qdir.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Note to maintainer:
+ -------------------
+
+ We load OpenSSL symbols dynamically. Because symbols are known to
+ disappear, and signatures sometimes change, between releases, we need to
+ be careful about how this is done. To ensure we don't end up dereferencing
+ null function pointers, and continue running even if certain functions are
+ missing, we define helper functions for each of the symbols we load from
+ OpenSSL, all prefixed with "q_" (declared in
+ qsslsocket_openssl_symbols_p.h). So instead of calling SSL_connect
+ directly, we call q_SSL_connect, which is a function that checks if the
+ actual SSL_connect fptr is null, and returns a failure if it is, or calls
+ SSL_connect if it isn't.
+
+ This requires a somewhat tedious process of declaring each function we
+ want to call in OpenSSL thrice: once with the q_, in _p.h, once using the
+ DEFINEFUNC macros below, and once in the function that actually resolves
+ the symbols, below the DEFINEFUNC declarations below.
+
+ There's one DEFINEFUNC macro declared for every number of arguments
+ exposed by OpenSSL (feel free to extend when needed). The easiest thing to
+ do is to find an existing entry that matches the arg count of the function
+ you want to import, and do the same.
+
+ The first macro arg is the function return type. The second is the
+ verbatim name of the function/symbol. Then follows a list of N pairs of
+ argument types with a variable name, and just the variable name (char *a,
+ a, char *b, b, etc). Finally there's two arguments - a suitable return
+ statement for the error case (for an int function, return 0 or return -1
+ is usually right). Then either just "return" or DUMMYARG, the latter being
+ for void functions.
+
+ Note: Take into account that these macros and declarations are processed
+ at compile-time, and the result depends on the OpenSSL headers the
+ compiling host has installed, but the symbols are resolved at run-time,
+ possibly with a different version of OpenSSL.
+*/
+
+#ifdef SSLEAY_MACROS
+DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return 0, return)
+#endif
+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)
+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)
+DEFINEFUNC2(BIO *, BIO_new_mem_buf, void *a, a, int b, b, return 0, return)
+DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
+DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
+DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
+DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
+DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG)
+DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG)
+DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG)
+DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, unsigned char **b, b, long c, c, return 0, return)
+#else // 0.9.8 broke SC and BC by changing this signature.
+DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return 0, return)
+#endif
+DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return 0, return)
+DEFINEFUNC(unsigned long, ERR_get_error, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const EVP_CIPHER *, EVP_des_ede3_cbc, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC3(int, EVP_PKEY_assign, EVP_PKEY *a, a, int b, b, char *c, c, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_RSA, EVP_PKEY *a, a, RSA *b, b, return -1, return)
+DEFINEFUNC2(int, EVP_PKEY_set1_DSA, EVP_PKEY *a, a, DSA *b, b, return -1, return)
+DEFINEFUNC(void, EVP_PKEY_free, EVP_PKEY *a, a, return, DUMMYARG)
+DEFINEFUNC(DSA *, EVP_PKEY_get1_DSA, EVP_PKEY *a, a, return 0, return)
+DEFINEFUNC(RSA *, EVP_PKEY_get1_RSA, EVP_PKEY *a, a, return 0, return)
+DEFINEFUNC(EVP_PKEY *, EVP_PKEY_new, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(int, EVP_PKEY_type, int a, a, return NID_undef, return)
+DEFINEFUNC2(int, i2d_X509, X509 *a, a, unsigned char **b, b, return -1, return)
+DEFINEFUNC(const char *, OBJ_nid2sn, int a, a, return 0, return)
+DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return)
+#ifdef SSLEAY_MACROS
+DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return)
+DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return)
+#else
+DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+#endif
+DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
+DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, return)
+DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return)
+DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
+DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
+DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
+DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return)
+DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG)
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG)
+DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return)
+#else
+DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG)
+DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return)
+#endif
+DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return)
+DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
+DEFINEFUNC3(char *, SSL_CIPHER_description, SSL_CIPHER *a, a, char *b, b, int c, c, return 0, return)
+DEFINEFUNC(int, SSL_connect, SSL *a, a, return -1, return)
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+DEFINEFUNC(int, SSL_CTX_check_private_key, const SSL_CTX *a, a, return -1, return)
+#else
+DEFINEFUNC(int, SSL_CTX_check_private_key, SSL_CTX *a, a, return -1, return)
+#endif
+DEFINEFUNC4(long, SSL_CTX_ctrl, SSL_CTX *a, a, int b, b, long c, c, void *d, d, return -1, return)
+DEFINEFUNC(void, SSL_CTX_free, SSL_CTX *a, a, return, DUMMYARG)
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return 0, return)
+#else
+DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return 0, return)
+#endif
+DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return)
+DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG)
+DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG)
+DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return)
+DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
+DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return)
+DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
+DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
+DEFINEFUNC(void, SSL_free, SSL *a, a, return, DUMMYARG)
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, const SSL *a, a, return 0, return)
+#else
+DEFINEFUNC(STACK_OF(SSL_CIPHER) *, SSL_get_ciphers, SSL *a, a, return 0, return)
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+DEFINEFUNC(const SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return)
+#else
+DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return)
+#endif
+DEFINEFUNC2(int, SSL_get_error, SSL *a, a, int b, b, return -1, return)
+DEFINEFUNC(STACK_OF(X509) *, SSL_get_peer_cert_chain, SSL *a, a, return 0, return)
+DEFINEFUNC(X509 *, SSL_get_peer_certificate, SSL *a, a, return 0, return)
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+DEFINEFUNC(long, SSL_get_verify_result, const SSL *a, a, return -1, return)
+#else
+DEFINEFUNC(long, SSL_get_verify_result, SSL *a, a, return -1, return)
+#endif
+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)
+DEFINEFUNC4(long, SSL_ctrl, SSL *a, a, int cmd, cmd, long larg, larg, const void *parg, parg, return -1, return)
+#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)
+DEFINEFUNC(void, SSL_set_connect_state, SSL *a, a, return, DUMMYARG)
+DEFINEFUNC(int, SSL_shutdown, SSL *a, a, return -1, return)
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(const SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return)
+#else
+DEFINEFUNC(SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv3_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv23_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, TLSv1_client_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv2_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv3_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, SSLv23_server_method, DUMMYARG, DUMMYARG, return 0, return)
+DEFINEFUNC(SSL_METHOD *, TLSv1_server_method, DUMMYARG, DUMMYARG, return 0, return)
+#endif
+DEFINEFUNC3(int, SSL_write, SSL *a, a, const void *b, b, int c, c, return -1, return)
+DEFINEFUNC2(int, X509_cmp, X509 *a, a, X509 *b, b, return -1, return)
+#ifndef SSLEAY_MACROS
+DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return 0, return)
+#endif
+DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return 0, return)
+DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
+DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return 0, return)
+DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
+DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return 0, return)
+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(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)
+DEFINEFUNC2(int, X509_STORE_add_cert, X509_STORE *a, a, X509 *b, b, return 0, return)
+DEFINEFUNC(void, X509_STORE_CTX_free, X509_STORE_CTX *a, a, return, DUMMYARG)
+DEFINEFUNC4(int, X509_STORE_CTX_init, X509_STORE_CTX *a, a, X509_STORE *b, b, X509 *c, c, STACK_OF(X509) *d, d, return -1, return)
+DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, return -1, return)
+DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return 0, return)
+#ifdef SSLEAY_MACROS
+DEFINEFUNC2(int, i2d_DSAPrivateKey, const DSA *a, a, unsigned char **b, b, return -1, return)
+DEFINEFUNC2(int, i2d_RSAPrivateKey, const RSA *a, a, unsigned char **b, b, return -1, return)
+DEFINEFUNC3(RSA *, d2i_RSAPrivateKey, RSA **a, a, unsigned char **b, b, long c, c, return 0, return)
+DEFINEFUNC3(DSA *, d2i_DSAPrivateKey, DSA **a, a, unsigned char **b, b, long c, c, return 0, return)
+#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) \
+ if (!(_q_##func = _q_PTR_##func(lib->resolve(#ordinal)))) \
+ qWarning("QSslSocket: cannot resolve "#func);
+#else
+#define RESOLVEFUNC(func) \
+ if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \
+ && !(_q_##func = _q_PTR_##func(libs.second->resolve(#func)))) \
+ qWarning("QSslSocket: cannot resolve "#func);
+#endif
+
+#if !defined QT_LINKED_OPENSSL
+
+#ifdef QT_NO_LIBRARY
+bool q_resolveOpenSslSymbols()
+{
+ qWarning("QSslSocket: unable to resolve symbols. "
+ "QT_NO_LIBRARY is defined which means runtime resolving of "
+ "libraries won't work.");
+ qWarning("Either compile Qt statically or with support for runtime resolving "
+ "of libraries.");
+ return false;
+}
+#else
+
+# ifdef Q_OS_UNIX
+static bool libGreaterThan(const QString &lhs, const QString &rhs)
+{
+ QStringList lhsparts = lhs.split(QLatin1Char('.'));
+ QStringList rhsparts = rhs.split(QLatin1Char('.'));
+ Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1);
+
+ for (int i = 1; i < rhsparts.count(); ++i) {
+ if (lhsparts.count() <= i)
+ // left hand side is shorter, so it's less than rhs
+ return false;
+
+ bool ok = false;
+ int b = 0;
+ int a = lhsparts.at(i).toInt(&ok);
+ if (ok)
+ b = rhsparts.at(i).toInt(&ok);
+ if (ok) {
+ // both toInt succeeded
+ if (a == b)
+ continue;
+ return a > b;
+ } else {
+ // compare as strings;
+ if (lhsparts.at(i) == rhsparts.at(i))
+ continue;
+ return lhsparts.at(i) > rhsparts.at(i);
+ }
+ }
+
+ // they compared strictly equally so far
+ // lhs cannot be less than rhs
+ return true;
+}
+
+static QStringList findAllLibSsl()
+{
+ QStringList paths;
+# ifdef Q_OS_DARWIN
+ paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH"))
+ .split(QLatin1Char(':'), QString::SkipEmptyParts);
+# else
+ paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH"))
+ .split(QLatin1Char(':'), QString::SkipEmptyParts);
+# endif
+ paths << QLatin1String("/lib") << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib");
+
+ QStringList foundSsls;
+ foreach (const QString &path, paths) {
+ QDir dir = QDir(path);
+ QStringList entryList = dir.entryList(QStringList() << QLatin1String("libssl.*"), QDir::Files);
+
+ qSort(entryList.begin(), entryList.end(), libGreaterThan);
+ foreach (const QString &entry, entryList)
+ foundSsls << path + QLatin1Char('/') + entry;
+ }
+
+ return foundSsls;
+}
+# endif
+
+#ifdef Q_OS_WIN
+static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32()
+{
+ QPair<QSystemLibrary*,QSystemLibrary*> pair;
+ pair.first = 0;
+ pair.second = 0;
+
+ QSystemLibrary *ssleay32 = new QSystemLibrary(QLatin1String("ssleay32"));
+ if (!ssleay32->load(false)) {
+ // Cannot find ssleay32.dll
+ delete ssleay32;
+ return pair;
+ }
+
+ QSystemLibrary *libeay32 = new QSystemLibrary(QLatin1String("libeay32"));
+ if (!libeay32->load(false)) {
+ delete ssleay32;
+ delete libeay32;
+ return pair;
+ }
+
+ pair.first = ssleay32;
+ pair.second = libeay32;
+ return pair;
+}
+#else
+
+static QPair<QLibrary*, QLibrary*> loadOpenSsl()
+{
+ QPair<QLibrary*,QLibrary*> pair;
+ pair.first = 0;
+ pair.second = 0;
+
+# if defined(Q_OS_SYMBIAN)
+ QLibrary *libssl = new QLibrary(QLatin1String("libssl"));
+ if (!libssl->load()) {
+ // Cannot find ssleay32.dll
+ delete libssl;
+ return pair;
+ }
+
+ QLibrary *libcrypto = new QLibrary(QLatin1String("libcrypto"));
+ if (!libcrypto->load()) {
+ delete libcrypto;
+ delete libssl;
+ return pair;
+ }
+
+ pair.first = libssl;
+ pair.second = libcrypto;
+ return pair;
+# elif defined(Q_OS_UNIX)
+ QLibrary *&libssl = pair.first;
+ QLibrary *&libcrypto = pair.second;
+ libssl = new QLibrary;
+ libcrypto = new QLibrary;
+
+ // Try to find the libssl library on the system.
+ //
+ // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that
+ // is, libssl.so on most Unix systems. However, the .so file isn't present in
+ // user installations because it's considered a development file.
+ //
+ // The right thing to do is to load the library at the major version we know how
+ // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h)
+ //
+ // However, OpenSSL is a well-known case of binary-compatibility breakage. To
+ // avoid such problems, many system integrators and Linux distributions change
+ // the soname of the binary, letting the full version number be the soname. So
+ // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that
+ // reason, we will search a few common paths (see findAllLibSsl() above) in hopes
+ // we find one that works.
+ //
+ // It is important, however, to try the canonical name and the unversioned name
+ // without going through the loop. By not specifying a path, we let the system
+ // dlopen(3) function determine it for us. This will include any DT_RUNPATH or
+ // DT_RPATH tags on our library header as well as other system-specific search
+ // paths. See the man page for dlopen(3) on your system for more information.
+
+#ifdef Q_OS_OPENBSD
+ libcrypto->setLoadHints(QLibrary::ExportExternalSymbolsHint);
+#endif
+#ifdef SHLIB_VERSION_NUMBER
+ // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER>
+ libssl->setFileNameAndVersion(QLatin1String("ssl"), QLatin1String(SHLIB_VERSION_NUMBER));
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto"), QLatin1String(SHLIB_VERSION_NUMBER));
+ if (libcrypto->load() && libssl->load()) {
+ // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found
+ return pair;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+#endif
+
+ // second attempt: find the development files libssl.so and libcrypto.so
+ libssl->setFileNameAndVersion(QLatin1String("ssl"), -1);
+ libcrypto->setFileNameAndVersion(QLatin1String("crypto"), -1);
+ if (libcrypto->load() && libssl->load()) {
+ // libssl.so.0 and libcrypto.so.0 found
+ return pair;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+
+ // third attempt: loop on the most common library paths and find libssl
+ QStringList sslList = findAllLibSsl();
+ foreach (const QString &ssl, sslList) {
+ QString crypto = ssl;
+ crypto.replace(QLatin1String("ssl"), QLatin1String("crypto"));
+ libssl->setFileNameAndVersion(ssl, -1);
+ libcrypto->setFileNameAndVersion(crypto, -1);
+ if (libcrypto->load() && libssl->load()) {
+ // libssl.so.0 and libcrypto.so.0 found
+ return pair;
+ } else {
+ libssl->unload();
+ libcrypto->unload();
+ }
+ }
+
+ // failed to load anything
+ delete libssl;
+ delete libcrypto;
+ libssl = libcrypto = 0;
+ return pair;
+
+# else
+ // not implemented for this platform yet
+ return pair;
+# endif
+}
+#endif
+
+bool q_resolveOpenSslSymbols()
+{
+ static volatile bool symbolsResolved = false;
+ static volatile bool triedToResolveSymbols = false;
+#ifndef QT_NO_THREAD
+ QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&q_SSL_library_init));
+#endif
+ if (symbolsResolved)
+ return true;
+ if (triedToResolveSymbols)
+ return false;
+ triedToResolveSymbols = true;
+
+#ifdef Q_OS_WIN
+ QPair<QSystemLibrary *, QSystemLibrary *> libs = loadOpenSslWin32();
+#else
+ QPair<QLibrary *, QLibrary *> libs = loadOpenSsl();
+#endif
+ if (!libs.first || !libs.second)
+ // failed to load them
+ return false;
+
+#ifdef Q_OS_SYMBIAN
+#ifdef SSLEAY_MACROS
+ RESOLVEFUNC(ASN1_dup, 125, libs.second )
+#endif
+ RESOLVEFUNC(ASN1_INTEGER_get, 48, libs.second )
+ RESOLVEFUNC(ASN1_STRING_data, 71, libs.second )
+ RESOLVEFUNC(ASN1_STRING_length, 76, libs.second )
+ RESOLVEFUNC(BIO_ctrl, 184, libs.second )
+ RESOLVEFUNC(BIO_free, 209, libs.second )
+ RESOLVEFUNC(BIO_new, 222, libs.second )
+ RESOLVEFUNC(BIO_new_mem_buf, 230, libs.second )
+ RESOLVEFUNC(BIO_read, 244, libs.second )
+ RESOLVEFUNC(BIO_s_mem, 251, libs.second )
+ RESOLVEFUNC(BIO_write, 269, libs.second )
+ RESOLVEFUNC(BN_num_bits, 387, libs.second )
+ RESOLVEFUNC(CRYPTO_free, 469, libs.second )
+ RESOLVEFUNC(CRYPTO_num_locks, 500, libs.second )
+ RESOLVEFUNC(CRYPTO_set_id_callback, 513, libs.second )
+ RESOLVEFUNC(CRYPTO_set_locking_callback, 516, libs.second )
+ RESOLVEFUNC(DSA_free, 594, libs.second )
+ RESOLVEFUNC(ERR_error_string, 744, libs.second )
+ RESOLVEFUNC(ERR_get_error, 749, libs.second )
+ RESOLVEFUNC(EVP_des_ede3_cbc, 919, libs.second )
+ RESOLVEFUNC(EVP_PKEY_assign, 859, libs.second )
+ RESOLVEFUNC(EVP_PKEY_set1_RSA, 880, libs.second )
+ RESOLVEFUNC(EVP_PKEY_set1_DSA, 879, libs.second )
+ RESOLVEFUNC(EVP_PKEY_free, 867, libs.second )
+ RESOLVEFUNC(EVP_PKEY_get1_DSA, 869, libs.second )
+ RESOLVEFUNC(EVP_PKEY_get1_RSA, 870, libs.second )
+ RESOLVEFUNC(EVP_PKEY_new, 876, libs.second )
+ RESOLVEFUNC(EVP_PKEY_type, 882, libs.second )
+ RESOLVEFUNC(OBJ_nid2sn, 1036, libs.second )
+ RESOLVEFUNC(OBJ_obj2nid, 1037, libs.second )
+#ifdef SSLEAY_MACROS // ### verify
+ RESOLVEFUNC(PEM_ASN1_read_bio, 1180, libs.second )
+#else
+ RESOLVEFUNC(PEM_read_bio_DSAPrivateKey, 1219, libs.second )
+ RESOLVEFUNC(PEM_read_bio_RSAPrivateKey, 1228, libs.second )
+ RESOLVEFUNC(PEM_write_bio_DSAPrivateKey, 1260, libs.second )
+ RESOLVEFUNC(PEM_write_bio_RSAPrivateKey, 1271, libs.second )
+#endif
+ RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY, 1220, libs.second )
+ RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY, 1230, libs.second )
+ RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY, 1261, libs.second )
+ RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY, 1273, libs.second )
+ RESOLVEFUNC(RAND_seed, 1426, libs.second )
+ RESOLVEFUNC(RAND_status, 1429, libs.second )
+ RESOLVEFUNC(RSA_free, 1450, libs.second )
+ RESOLVEFUNC(sk_free, 2571, libs.second )
+ RESOLVEFUNC(sk_num, 2576, libs.second )
+ RESOLVEFUNC(sk_pop_free, 2578, libs.second )
+ RESOLVEFUNC(sk_value, 2585, libs.second )
+ RESOLVEFUNC(SSL_CIPHER_description, 11, libs.first )
+ RESOLVEFUNC(SSL_CTX_check_private_key, 21, libs.first )
+ RESOLVEFUNC(SSL_CTX_ctrl, 22, libs.first )
+ RESOLVEFUNC(SSL_CTX_free, 24, libs.first )
+ RESOLVEFUNC(SSL_CTX_new, 35, libs.first )
+ RESOLVEFUNC(SSL_CTX_set_cipher_list, 40, libs.first )
+ RESOLVEFUNC(SSL_CTX_set_default_verify_paths, 44, libs.first )
+ RESOLVEFUNC(SSL_CTX_set_verify, 56, libs.first )
+ RESOLVEFUNC(SSL_CTX_set_verify_depth, 57, libs.first )
+ RESOLVEFUNC(SSL_CTX_use_certificate, 64, libs.first )
+ RESOLVEFUNC(SSL_CTX_use_certificate_file, 67, libs.first )
+ RESOLVEFUNC(SSL_CTX_use_PrivateKey, 58, libs.first )
+ RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey, 61, libs.first )
+ RESOLVEFUNC(SSL_CTX_use_PrivateKey_file, 60, libs.first )
+ RESOLVEFUNC(SSL_accept, 82, libs.first )
+ RESOLVEFUNC(SSL_clear, 92, libs.first )
+ RESOLVEFUNC(SSL_connect, 93, libs.first )
+ RESOLVEFUNC(SSL_free, 99, libs.first )
+ RESOLVEFUNC(SSL_get_ciphers, 104, libs.first )
+ RESOLVEFUNC(SSL_get_current_cipher, 106, libs.first )
+ RESOLVEFUNC(SSL_get_error, 110, libs.first )
+ RESOLVEFUNC(SSL_get_peer_cert_chain, 117, libs.first )
+ RESOLVEFUNC(SSL_get_peer_certificate, 118, libs.first )
+ RESOLVEFUNC(SSL_get_verify_result, 132, libs.first )
+ 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 )
+ RESOLVEFUNC(SSL_set_connect_state, 152, libs.first )
+ RESOLVEFUNC(SSL_shutdown, 173, libs.first )
+ RESOLVEFUNC(SSL_write, 188, libs.first )
+ RESOLVEFUNC(SSLv2_client_method, 192, libs.first )
+ RESOLVEFUNC(SSLv3_client_method, 195, libs.first )
+ RESOLVEFUNC(SSLv23_client_method, 189, libs.first )
+ RESOLVEFUNC(TLSv1_client_method, 198, libs.first )
+ RESOLVEFUNC(SSLv2_server_method, 194, libs.first )
+ RESOLVEFUNC(SSLv3_server_method, 197, libs.first )
+ RESOLVEFUNC(SSLv23_server_method, 191, libs.first )
+ RESOLVEFUNC(TLSv1_server_method, 200, libs.first )
+ RESOLVEFUNC(SSL_CTX_load_verify_locations, 34, libs.first )
+ RESOLVEFUNC(X509_NAME_oneline, 1830, libs.second )
+ RESOLVEFUNC(X509_PUBKEY_get, 1844, libs.second )
+ RESOLVEFUNC(X509_STORE_free, 1939, libs.second )
+ RESOLVEFUNC(X509_STORE_new, 1942, libs.second )
+ RESOLVEFUNC(X509_STORE_add_cert, 1936, libs.second )
+ RESOLVEFUNC(X509_STORE_CTX_free, 1907, libs.second )
+ RESOLVEFUNC(X509_STORE_CTX_init, 1919, libs.second )
+ RESOLVEFUNC(X509_STORE_CTX_new, 1920, libs.second )
+ RESOLVEFUNC(X509_STORE_CTX_set_purpose, 1931, libs.second )
+ RESOLVEFUNC(X509_cmp, 1992, libs.second )
+#ifndef SSLEAY_MACROS
+ RESOLVEFUNC(X509_dup, 1997, libs.second )
+#endif
+ RESOLVEFUNC(X509_EXTENSION_get_object, 1785, libs.second )
+ RESOLVEFUNC(X509_free, 2001, libs.second )
+ RESOLVEFUNC(X509_get_ext, 2012, libs.second )
+ RESOLVEFUNC(X509_get_ext_count, 2016, libs.second )
+ RESOLVEFUNC(X509_get_ext_d2i, 2017, libs.second )
+ RESOLVEFUNC(X509_get_issuer_name, 2018, libs.second )
+ RESOLVEFUNC(X509_get_subject_name, 2022, libs.second )
+ RESOLVEFUNC(X509_verify_cert, 2069, libs.second )
+ RESOLVEFUNC(d2i_X509, 2309, libs.second )
+ RESOLVEFUNC(i2d_X509, 2489, libs.second )
+#ifdef SSLEAY_MACROS
+ RESOLVEFUNC(i2d_DSAPrivateKey, 2395, libs.second )
+ RESOLVEFUNC(i2d_RSAPrivateKey, 2476, libs.second )
+ RESOLVEFUNC(d2i_DSAPrivateKey, 2220, libs.second )
+ RESOLVEFUNC(d2i_RSAPrivateKey, 2296, libs.second )
+#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)
+#endif
+ RESOLVEFUNC(ASN1_INTEGER_get)
+ RESOLVEFUNC(ASN1_STRING_data)
+ RESOLVEFUNC(ASN1_STRING_length)
+ RESOLVEFUNC(BIO_ctrl)
+ RESOLVEFUNC(BIO_free)
+ RESOLVEFUNC(BIO_new)
+ RESOLVEFUNC(BIO_new_mem_buf)
+ RESOLVEFUNC(BIO_read)
+ RESOLVEFUNC(BIO_s_mem)
+ RESOLVEFUNC(BIO_write)
+ RESOLVEFUNC(BN_num_bits)
+ RESOLVEFUNC(CRYPTO_free)
+ RESOLVEFUNC(CRYPTO_num_locks)
+ RESOLVEFUNC(CRYPTO_set_id_callback)
+ RESOLVEFUNC(CRYPTO_set_locking_callback)
+ RESOLVEFUNC(DSA_free)
+ RESOLVEFUNC(ERR_error_string)
+ RESOLVEFUNC(ERR_get_error)
+ RESOLVEFUNC(EVP_des_ede3_cbc)
+ RESOLVEFUNC(EVP_PKEY_assign)
+ RESOLVEFUNC(EVP_PKEY_set1_RSA)
+ RESOLVEFUNC(EVP_PKEY_set1_DSA)
+ RESOLVEFUNC(EVP_PKEY_free)
+ RESOLVEFUNC(EVP_PKEY_get1_DSA)
+ RESOLVEFUNC(EVP_PKEY_get1_RSA)
+ RESOLVEFUNC(EVP_PKEY_new)
+ RESOLVEFUNC(EVP_PKEY_type)
+ RESOLVEFUNC(OBJ_nid2sn)
+ RESOLVEFUNC(OBJ_obj2nid)
+#ifdef SSLEAY_MACROS // ### verify
+ RESOLVEFUNC(PEM_ASN1_read_bio)
+#else
+ RESOLVEFUNC(PEM_read_bio_DSAPrivateKey)
+ RESOLVEFUNC(PEM_read_bio_RSAPrivateKey)
+ RESOLVEFUNC(PEM_write_bio_DSAPrivateKey)
+ RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
+#endif
+ RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY)
+ RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY)
+ RESOLVEFUNC(PEM_write_bio_DSA_PUBKEY)
+ RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY)
+ RESOLVEFUNC(RAND_seed)
+ RESOLVEFUNC(RAND_status)
+ RESOLVEFUNC(RSA_free)
+ RESOLVEFUNC(sk_free)
+ RESOLVEFUNC(sk_num)
+ RESOLVEFUNC(sk_pop_free)
+ RESOLVEFUNC(sk_value)
+ RESOLVEFUNC(SSL_CIPHER_description)
+ RESOLVEFUNC(SSL_CTX_check_private_key)
+ RESOLVEFUNC(SSL_CTX_ctrl)
+ RESOLVEFUNC(SSL_CTX_free)
+ RESOLVEFUNC(SSL_CTX_new)
+ RESOLVEFUNC(SSL_CTX_set_cipher_list)
+ RESOLVEFUNC(SSL_CTX_set_default_verify_paths)
+ RESOLVEFUNC(SSL_CTX_set_verify)
+ RESOLVEFUNC(SSL_CTX_set_verify_depth)
+ RESOLVEFUNC(SSL_CTX_use_certificate)
+ RESOLVEFUNC(SSL_CTX_use_certificate_file)
+ RESOLVEFUNC(SSL_CTX_use_PrivateKey)
+ RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey)
+ RESOLVEFUNC(SSL_CTX_use_PrivateKey_file)
+ RESOLVEFUNC(SSL_accept)
+ RESOLVEFUNC(SSL_clear)
+ RESOLVEFUNC(SSL_connect)
+ RESOLVEFUNC(SSL_free)
+ RESOLVEFUNC(SSL_get_ciphers)
+ RESOLVEFUNC(SSL_get_current_cipher)
+ RESOLVEFUNC(SSL_get_error)
+ RESOLVEFUNC(SSL_get_peer_cert_chain)
+ RESOLVEFUNC(SSL_get_peer_certificate)
+ RESOLVEFUNC(SSL_get_verify_result)
+ 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)
+ RESOLVEFUNC(SSL_set_connect_state)
+ RESOLVEFUNC(SSL_shutdown)
+ RESOLVEFUNC(SSL_write)
+ RESOLVEFUNC(SSLv2_client_method)
+ RESOLVEFUNC(SSLv3_client_method)
+ RESOLVEFUNC(SSLv23_client_method)
+ RESOLVEFUNC(TLSv1_client_method)
+ RESOLVEFUNC(SSLv2_server_method)
+ RESOLVEFUNC(SSLv3_server_method)
+ RESOLVEFUNC(SSLv23_server_method)
+ RESOLVEFUNC(TLSv1_server_method)
+ RESOLVEFUNC(X509_NAME_oneline)
+ RESOLVEFUNC(X509_PUBKEY_get)
+ RESOLVEFUNC(X509_STORE_free)
+ RESOLVEFUNC(X509_STORE_new)
+ RESOLVEFUNC(X509_STORE_add_cert)
+ RESOLVEFUNC(X509_STORE_CTX_free)
+ RESOLVEFUNC(X509_STORE_CTX_init)
+ RESOLVEFUNC(X509_STORE_CTX_new)
+ RESOLVEFUNC(X509_STORE_CTX_set_purpose)
+ RESOLVEFUNC(X509_cmp)
+#ifndef SSLEAY_MACROS
+ RESOLVEFUNC(X509_dup)
+#endif
+ RESOLVEFUNC(X509_EXTENSION_get_object)
+ RESOLVEFUNC(X509_free)
+ RESOLVEFUNC(X509_get_ext)
+ RESOLVEFUNC(X509_get_ext_count)
+ RESOLVEFUNC(X509_get_ext_d2i)
+ RESOLVEFUNC(X509_get_issuer_name)
+ RESOLVEFUNC(X509_get_subject_name)
+ RESOLVEFUNC(X509_verify_cert)
+ RESOLVEFUNC(d2i_X509)
+ RESOLVEFUNC(i2d_X509)
+#ifdef SSLEAY_MACROS
+ RESOLVEFUNC(i2d_DSAPrivateKey)
+ RESOLVEFUNC(i2d_RSAPrivateKey)
+ RESOLVEFUNC(d2i_DSAPrivateKey)
+ RESOLVEFUNC(d2i_RSAPrivateKey)
+#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;
+ delete libs.second;
+ return true;
+}
+#endif // QT_NO_LIBRARY
+
+#else // !defined QT_LINKED_OPENSSL
+
+bool q_resolveOpenSslSymbols()
+{
+#ifdef QT_NO_OPENSSL
+ return false;
+#endif
+ return true;
+}
+#endif // !defined QT_LINKED_OPENSSL
+
+//==============================================================================
+// contributed by Jay Case of Sarvega, Inc.; http://sarvega.com/
+// Based on X509_cmp_time() for intitial buffer hacking.
+//==============================================================================
+QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
+{
+ size_t lTimeLength = aTime->length;
+ char *pString = (char *) aTime->data;
+
+ if (aTime->type == V_ASN1_UTCTIME) {
+
+ char lBuffer[24];
+ char *pBuffer = lBuffer;
+
+ if ((lTimeLength < 11) || (lTimeLength > 17))
+ return QDateTime();
+
+ memcpy(pBuffer, pString, 10);
+ pBuffer += 10;
+ pString += 10;
+
+ if ((*pString == 'Z') || (*pString == '-') || (*pString == '+')) {
+ *pBuffer++ = '0';
+ *pBuffer++ = '0';
+ } else {
+ *pBuffer++ = *pString++;
+ *pBuffer++ = *pString++;
+ // Skip any fractional seconds...
+ if (*pString == '.') {
+ pString++;
+ while ((*pString >= '0') && (*pString <= '9'))
+ pString++;
+ }
+ }
+
+ *pBuffer++ = 'Z';
+ *pBuffer++ = '\0';
+
+ time_t lSecondsFromUCT;
+ if (*pString == 'Z') {
+ lSecondsFromUCT = 0;
+ } else {
+ if ((*pString != '+') && (*pString != '-'))
+ return QDateTime();
+
+ lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
+ lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0');
+ lSecondsFromUCT *= 60;
+ if (*pString == '-')
+ lSecondsFromUCT = -lSecondsFromUCT;
+ }
+
+ tm lTime;
+ lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
+ lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
+ lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
+ lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
+ lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
+ lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
+ if (lTime.tm_year < 50)
+ lTime.tm_year += 100; // RFC 2459
+
+ QDate resDate(lTime.tm_year + 1900, lTime.tm_mon + 1, lTime.tm_mday);
+ QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
+
+ QDateTime result(resDate, resTime, Qt::UTC);
+ result = result.addSecs(lSecondsFromUCT);
+ return result;
+
+ } else if (aTime->type == V_ASN1_GENERALIZEDTIME) {
+
+ if (lTimeLength < 15)
+ return QDateTime(); // hopefully never triggered
+
+ // generalized time is always YYYYMMDDHHMMSSZ (RFC 2459, section 4.1.2.5.2)
+ tm lTime;
+ lTime.tm_sec = ((pString[12] - '0') * 10) + (pString[13] - '0');
+ lTime.tm_min = ((pString[10] - '0') * 10) + (pString[11] - '0');
+ lTime.tm_hour = ((pString[8] - '0') * 10) + (pString[9] - '0');
+ lTime.tm_mday = ((pString[6] - '0') * 10) + (pString[7] - '0');
+ lTime.tm_mon = (((pString[4] - '0') * 10) + (pString[5] - '0'));
+ lTime.tm_year = ((pString[0] - '0') * 1000) + ((pString[1] - '0') * 100) +
+ ((pString[2] - '0') * 10) + (pString[3] - '0');
+
+ QDate resDate(lTime.tm_year, lTime.tm_mon, lTime.tm_mday);
+ QTime resTime(lTime.tm_hour, lTime.tm_min, lTime.tm_sec);
+
+ QDateTime result(resDate, resTime, Qt::UTC);
+ return result;
+
+ } else {
+ qWarning("unsupported date format detected");
+ return QDateTime();
+ }
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
new file mode 100644
index 0000000000..49830acc1e
--- /dev/null
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -0,0 +1,427 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLSOCKET_OPENSSL_SYMBOLS_P_H
+#define QSSLSOCKET_OPENSSL_SYMBOLS_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 "qsslsocket_openssl_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#define DUMMYARG
+
+#if !defined QT_LINKED_OPENSSL
+// **************** Shared declarations ******************
+// ret func(arg)
+
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a); \
+ }
+
+// ret func(arg1, arg2)
+# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func);\
+ err; \
+ } \
+ funcret _q_##func(a, b); \
+ }
+
+// ret func(arg1, arg2, arg3)
+# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4)
+# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg5)
+# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6)
+# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
+# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { \
+ if (!_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f, g); \
+ }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
+# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
+ typedef ret (*_q_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); \
+ static _q_PTR_##func _q_##func = 0; \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { \
+ if (_q_##func) { \
+ qWarning("QSslSocket: cannot call unresolved function "#func); \
+ err; \
+ } \
+ funcret _q_##func(a, b, c, d, e, f, g, h, i); \
+ }
+// **************** Shared declarations ******************
+
+#else // !defined QT_LINKED_OPENSSL
+
+// **************** Static declarations ******************
+
+// ret func(arg)
+# define DEFINEFUNC(ret, func, arg, a, err, funcret) \
+ ret q_##func(arg) { funcret func(a); }
+
+// ret func(arg1, arg2)
+# define DEFINEFUNC2(ret, func, arg1, a, arg2, b, err, funcret) \
+ ret q_##func(arg1, arg2) { funcret func(a, b); }
+
+// ret func(arg1, arg2, arg3)
+# define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \
+ ret q_##func(arg1, arg2, arg3) { funcret func(a, b, c); }
+
+// ret func(arg1, arg2, arg3, arg4)
+# define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4) { funcret func(a, b, c, d); }
+
+// ret func(arg1, arg2, arg3, arg4, arg5)
+# define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5) { funcret func(a, b, c, d, e); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6)
+# define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6) { funcret func(a, b, c, d, e, f); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7)
+# define DEFINEFUNC7(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { funcret func(a, b, c, d, e, f, g); }
+
+// ret func(arg1, arg2, arg3, arg4, arg6, arg7, arg8, arg9)
+# define DEFINEFUNC9(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, arg7, g, arg8, h, arg9, i, err, funcret) \
+ ret q_##func(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { funcret func(a, b, c, d, e, f, g, h, i); }
+
+// **************** Static declarations ******************
+
+#endif // !defined QT_LINKED_OPENSSL
+
+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);
+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);
+BIO *q_BIO_new_mem_buf(void *a, int b);
+int q_BIO_read(BIO *a, void *b, int c);
+BIO_METHOD *q_BIO_s_mem();
+int q_BIO_write(BIO *a, const void *b, int c);
+int q_BN_num_bits(const BIGNUM *a);
+int q_CRYPTO_num_locks();
+void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int));
+void q_CRYPTO_set_id_callback(unsigned long (*a)());
+void q_CRYPTO_free(void *a);
+void q_DSA_free(DSA *a);
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
+#else
+X509 *q_d2i_X509(X509 **a, unsigned char **b, long c);
+#endif
+char *q_ERR_error_string(unsigned long a, char *b);
+unsigned long q_ERR_get_error();
+const EVP_CIPHER *q_EVP_des_ede3_cbc();
+int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c);
+int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
+int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
+void q_EVP_PKEY_free(EVP_PKEY *a);
+RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
+DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
+int q_EVP_PKEY_type(int a);
+EVP_PKEY *q_EVP_PKEY_new();
+int q_i2d_X509(X509 *a, unsigned char **b);
+const char *q_OBJ_nid2sn(int a);
+int q_OBJ_obj2nid(const ASN1_OBJECT *a);
+#ifdef SSLEAY_MACROS
+// ### verify
+void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_password_cb *e,
+ void *f);
+// ### ditto for write
+#else
+DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d);
+RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d);
+int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
+ int e, pem_password_cb *f, void *g);
+#endif
+DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
+RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
+int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b);
+int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b);
+void q_RAND_seed(const void *a, int b);
+int q_RAND_status();
+void q_RSA_free(RSA *a);
+int q_sk_num(STACK *a);
+void q_sk_pop_free(STACK *a, void (*b)(void *));
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+void q_sk_free(_STACK *a);
+void * q_sk_value(STACK *a, int b);
+#else
+void q_sk_free(STACK *a);
+char * q_sk_value(STACK *a, int b);
+#endif
+int q_SSL_accept(SSL *a);
+int q_SSL_clear(SSL *a);
+char *q_SSL_CIPHER_description(SSL_CIPHER *a, char *b, int c);
+int q_SSL_connect(SSL *a);
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+int q_SSL_CTX_check_private_key(const SSL_CTX *a);
+#else
+int q_SSL_CTX_check_private_key(SSL_CTX *a);
+#endif
+long q_SSL_CTX_ctrl(SSL_CTX *a, int b, long c, void *d);
+void q_SSL_CTX_free(SSL_CTX *a);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
+#else
+SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a);
+#endif
+int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b);
+int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a);
+void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *));
+void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b);
+int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b);
+int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
+int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
+int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b);
+int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c);
+void q_SSL_free(SSL *a);
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(const SSL *a);
+#else
+STACK_OF(SSL_CIPHER) *q_SSL_get_ciphers(SSL *a);
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+const SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
+#else
+SSL_CIPHER *q_SSL_get_current_cipher(SSL *a);
+#endif
+int q_SSL_get_error(SSL *a, int b);
+STACK_OF(X509) *q_SSL_get_peer_cert_chain(SSL *a);
+X509 *q_SSL_get_peer_certificate(SSL *a);
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+// 0.9.8 broke SC and BC by changing this function's signature.
+long q_SSL_get_verify_result(const SSL *a);
+#else
+long q_SSL_get_verify_result(SSL *a);
+#endif
+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)
+long q_SSL_ctrl(SSL *ssl,int cmd, long larg, const void *parg);
+#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);
+void q_SSL_set_connect_state(SSL *a);
+int q_SSL_shutdown(SSL *a);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+const SSL_METHOD *q_SSLv2_client_method();
+const SSL_METHOD *q_SSLv3_client_method();
+const SSL_METHOD *q_SSLv23_client_method();
+const SSL_METHOD *q_TLSv1_client_method();
+const SSL_METHOD *q_SSLv2_server_method();
+const SSL_METHOD *q_SSLv3_server_method();
+const SSL_METHOD *q_SSLv23_server_method();
+const SSL_METHOD *q_TLSv1_server_method();
+#else
+SSL_METHOD *q_SSLv2_client_method();
+SSL_METHOD *q_SSLv3_client_method();
+SSL_METHOD *q_SSLv23_client_method();
+SSL_METHOD *q_TLSv1_client_method();
+SSL_METHOD *q_SSLv2_server_method();
+SSL_METHOD *q_SSLv3_server_method();
+SSL_METHOD *q_SSLv23_server_method();
+SSL_METHOD *q_TLSv1_server_method();
+#endif
+int q_SSL_write(SSL *a, const void *b, int c);
+int q_X509_cmp(X509 *a, X509 *b);
+#ifdef SSLEAY_MACROS
+void *q_ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x);
+#define q_X509_dup(x509) (X509 *)q_ASN1_dup((i2d_of_void *)q_i2d_X509, \
+ (d2i_of_void *)q_d2i_X509,(char *)x509)
+#else
+X509 *q_X509_dup(X509 *a);
+#endif
+ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a);
+void q_X509_free(X509 *a);
+X509_EXTENSION *q_X509_get_ext(X509 *a, int b);
+int q_X509_get_ext_count(X509 *a);
+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);
+EVP_PKEY *q_X509_PUBKEY_get(X509_PUBKEY *a);
+void q_X509_STORE_free(X509_STORE *store);
+X509_STORE *q_X509_STORE_new();
+int q_X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
+void q_X509_STORE_CTX_free(X509_STORE_CTX *storeCtx);
+int q_X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
+ X509 *x509, STACK_OF(X509) *chain);
+X509_STORE_CTX *q_X509_STORE_CTX_new();
+int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
+
+#define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp)
+#define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
+#ifdef SSLEAY_MACROS
+int q_i2d_DSAPrivateKey(const DSA *a, unsigned char **pp);
+int q_i2d_RSAPrivateKey(const RSA *a, unsigned char **pp);
+RSA *q_d2i_RSAPrivateKey(RSA **a, unsigned char **pp, long length);
+DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
+#define q_PEM_read_bio_RSAPrivateKey(bp, x, cb, u) \
+ (RSA *)q_PEM_ASN1_read_bio( \
+ (void *(*)(void**, const unsigned char**, long int))q_d2i_RSAPrivateKey, PEM_STRING_RSA, bp, (void **)x, cb, u)
+#define q_PEM_read_bio_DSAPrivateKey(bp, x, cb, u) \
+ (DSA *)q_PEM_ASN1_read_bio( \
+ (void *(*)(void**, const unsigned char**, long int))q_d2i_DSAPrivateKey, PEM_STRING_DSA, bp, (void **)x, cb, u)
+#define q_PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_RSAPrivateKey,PEM_STRING_RSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
+#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
+ PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\
+ bp,(char *)x,enc,kstr,klen,cb,u)
+#endif
+#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
+#define q_SKM_sk_num(type, st) ((int (*)(const STACK_OF(type) *))q_sk_num)(st)
+#define q_SKM_sk_value(type, st,i) ((type * (*)(const STACK_OF(type) *, int))q_sk_value)(st, i)
+#define q_sk_GENERAL_NAME_num(st) q_SKM_sk_num(GENERAL_NAME, (st))
+#define q_sk_GENERAL_NAME_value(st, i) q_SKM_sk_value(GENERAL_NAME, (st), (i))
+#define q_sk_X509_num(st) q_SKM_sk_num(X509, (st))
+#define q_sk_X509_value(st, i) q_SKM_sk_value(X509, (st), (i))
+#define q_sk_SSL_CIPHER_num(st) q_SKM_sk_num(SSL_CIPHER, (st))
+#define q_sk_SSL_CIPHER_value(st, i) q_SKM_sk_value(SSL_CIPHER, (st), (i))
+#define q_SSL_CTX_add_extra_chain_cert(ctx,x509) \
+ q_SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509)
+#define q_X509_get_notAfter(x) X509_get_notAfter(x)
+#define q_X509_get_notBefore(x) X509_get_notBefore(x)
+#define q_EVP_PKEY_assign_RSA(pkey,rsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
+ (char *)(rsa))
+#define q_EVP_PKEY_assign_DSA(pkey,dsa) q_EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\
+ (char *)(dsa))
+#ifdef OPENSSL_LOAD_CONF
+#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_conf()
+#else
+#define q_OpenSSL_add_all_algorithms() q_OPENSSL_add_all_algorithms_noconf()
+#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;
+QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
new file mode 100644
index 0000000000..4662c56ec4
--- /dev/null
+++ b/src/network/ssl/qsslsocket_p.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSSLSOCKET_P_H
+#define QSSLSOCKET_P_H
+
+#include "qsslsocket.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 <private/qtcpsocket_p.h>
+#include "qsslkey.h"
+#include "qsslconfiguration_p.h"
+
+#include <QtCore/qstringlist.h>
+
+#include <private/qringbuffer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_MAC)
+#include <Security/SecCertificate.h>
+#include <CoreFoundation/CFArray.h>
+ typedef OSStatus (*PtrSecCertificateGetData)(SecCertificateRef, CSSM_DATA_PTR);
+ typedef OSStatus (*PtrSecTrustSettingsCopyCertificates)(int, CFArrayRef*);
+ typedef OSStatus (*PtrSecTrustCopyAnchorCertificates)(CFArrayRef*);
+#elif defined(Q_OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#ifndef HCRYPTPROV_LEGACY
+#define HCRYPTPROV_LEGACY HCRYPTPROV
+#endif
+#if defined(Q_OS_WINCE)
+ typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(LPCSTR, DWORD, HCRYPTPROV_LEGACY, DWORD, const void*);
+#else
+ typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(HCRYPTPROV_LEGACY, LPCWSTR);
+#endif
+ typedef PCCERT_CONTEXT (WINAPI *PtrCertFindCertificateInStore)(HCERTSTORE, DWORD, DWORD, DWORD, const void*, PCCERT_CONTEXT);
+ typedef BOOL (WINAPI *PtrCertCloseStore)(HCERTSTORE, DWORD);
+#endif
+
+
+
+class QSslSocketPrivate : public QTcpSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QSslSocket)
+public:
+ QSslSocketPrivate();
+ virtual ~QSslSocketPrivate();
+
+ void init();
+ bool initialized;
+
+ QSslSocket::SslMode mode;
+ bool autoStartHandshake;
+ bool connectionEncrypted;
+ bool ignoreAllSslErrors;
+ QList<QSslError> ignoreErrorsList;
+ bool* readyReadEmittedPointer;
+
+ QSslConfigurationPrivate configuration;
+ QList<QSslError> sslErrors;
+
+ // if set, this hostname is used for certificate validation instead of the hostname
+ // that was used for connecting to.
+ QString verificationPeerName;
+
+ bool allowRootCertOnDemandLoading;
+
+ static bool supportsSsl();
+ static void ensureInitialized();
+ static void deinitialize();
+ static QList<QSslCipher> defaultCiphers();
+ static QList<QSslCipher> supportedCiphers();
+ static void setDefaultCiphers(const QList<QSslCipher> &ciphers);
+ static void setDefaultSupportedCiphers(const QList<QSslCipher> &ciphers);
+ static void resetDefaultCiphers();
+
+ static QList<QSslCertificate> defaultCaCertificates();
+ static QList<QSslCertificate> systemCaCertificates();
+ static void setDefaultCaCertificates(const QList<QSslCertificate> &certs);
+ static bool addDefaultCaCertificates(const QString &path, QSsl::EncodingFormat format,
+ QRegExp::PatternSyntax syntax);
+ static void addDefaultCaCertificate(const QSslCertificate &cert);
+ static void addDefaultCaCertificates(const QList<QSslCertificate> &certs);
+
+#if defined(Q_OS_MAC)
+ static PtrSecCertificateGetData ptrSecCertificateGetData;
+ static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates;
+ static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates;
+#elif defined(Q_OS_WIN)
+ static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW;
+ static PtrCertFindCertificateInStore ptrCertFindCertificateInStore;
+ static PtrCertCloseStore ptrCertCloseStore;
+#endif
+
+ // The socket itself, including private slots.
+ QTcpSocket *plainSocket;
+ void createPlainSocket(QIODevice::OpenMode openMode);
+ static void pauseSocketNotifiers(QSslSocket*);
+ static void resumeSocketNotifiers(QSslSocket*);
+ void _q_connectedSlot();
+ void _q_hostFoundSlot();
+ void _q_disconnectedSlot();
+ void _q_stateChangedSlot(QAbstractSocket::SocketState);
+ void _q_errorSlot(QAbstractSocket::SocketError);
+ void _q_readyReadSlot();
+ void _q_bytesWrittenSlot(qint64);
+ void _q_flushWriteBuffer();
+ void _q_flushReadBuffer();
+
+ // Platform specific functions
+ virtual void startClientEncryption() = 0;
+ virtual void startServerEncryption() = 0;
+ virtual void transmit() = 0;
+ virtual void disconnectFromHost() = 0;
+ virtual void disconnected() = 0;
+ virtual QSslCipher sessionCipher() const = 0;
+
+private:
+ static bool ensureLibraryLoaded();
+ static void ensureCiphersAndCertsLoaded();
+
+ static bool s_libraryLoaded;
+ static bool s_loadedCiphersAndCerts;
+protected:
+ static bool s_loadRootCertsOnDemand;
+ static QList<QByteArray> unixRootCertDirectories();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
new file mode 100644
index 0000000000..8b2e2c1917
--- /dev/null
+++ b/src/network/ssl/ssl.pri
@@ -0,0 +1,36 @@
+# OpenSSL support; compile in QSslSocket.
+contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
+
+
+symbian {
+ INCLUDEPATH *= $$OS_LAYER_SSL_SYSTEMINCLUDE
+} else {
+ include($$QT_SOURCE_TREE/config.tests/unix/openssl/openssl.pri)
+}
+
+ HEADERS += ssl/qssl.h \
+ ssl/qsslcertificate.h \
+ ssl/qsslcertificate_p.h \
+ ssl/qsslconfiguration.h \
+ ssl/qsslconfiguration_p.h \
+ ssl/qsslcipher.h \
+ ssl/qsslcipher_p.h \
+ ssl/qsslerror.h \
+ ssl/qsslkey.h \
+ ssl/qsslsocket.h \
+ ssl/qsslsocket_openssl_p.h \
+ ssl/qsslsocket_openssl_symbols_p.h \
+ ssl/qsslsocket_p.h
+ SOURCES += ssl/qssl.cpp \
+ ssl/qsslcertificate.cpp \
+ ssl/qsslconfiguration.cpp \
+ ssl/qsslcipher.cpp \
+ ssl/qsslerror.cpp \
+ ssl/qsslkey.cpp \
+ ssl/qsslsocket.cpp \
+ ssl/qsslsocket_openssl.cpp \
+ ssl/qsslsocket_openssl_symbols.cpp
+
+ # Add optional SSL libs
+ LIBS_PRIVATE += $$OPENSSL_LIBS
+}