diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/network | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'src/network')
128 files changed, 54227 insertions, 0 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri new file mode 100644 index 0000000..6bcca0c --- /dev/null +++ b/src/network/access/access.pri @@ -0,0 +1,53 @@ +# Qt network access module + +HEADERS += access/qftp.h \ + access/qhttp.h \ + access/qhttpnetworkconnection_p.h \ + access/qnetworkaccessmanager.h \ + access/qnetworkaccessmanager_p.h \ + access/qnetworkaccesscache_p.h \ + access/qnetworkaccessbackend_p.h \ + access/qnetworkaccessdatabackend_p.h \ + access/qnetworkaccessdebugpipebackend_p.h \ + access/qnetworkaccesshttpbackend_p.h \ + access/qnetworkaccessfilebackend_p.h \ + access/qnetworkaccesscachebackend_p.h \ + access/qnetworkaccessftpbackend_p.h \ + access/qnetworkcookie.h \ + access/qnetworkcookie_p.h \ + access/qnetworkrequest.h \ + access/qnetworkrequest_p.h \ + access/qnetworkreply.h \ + access/qnetworkreply_p.h \ + access/qnetworkreplyimpl_p.h \ + access/qabstractnetworkcache_p.h \ + access/qabstractnetworkcache.h \ + access/qnetworkdiskcache_p.h \ + access/qnetworkdiskcache.h + +SOURCES += access/qftp.cpp \ + access/qhttp.cpp \ + access/qhttpnetworkconnection.cpp \ + access/qnetworkaccessmanager.cpp \ + access/qnetworkaccesscache.cpp \ + access/qnetworkaccessbackend.cpp \ + access/qnetworkaccessdatabackend.cpp \ + access/qnetworkaccessdebugpipebackend.cpp \ + access/qnetworkaccessfilebackend.cpp \ + access/qnetworkaccesscachebackend.cpp \ + access/qnetworkaccessftpbackend.cpp \ + access/qnetworkaccesshttpbackend.cpp \ + access/qnetworkcookie.cpp \ + access/qnetworkrequest.cpp \ + access/qnetworkreply.cpp \ + access/qnetworkreplyimpl.cpp \ + access/qabstractnetworkcache.cpp \ + access/qnetworkdiskcache.cpp + +#zlib support +contains(QT_CONFIG, zlib) { + INCLUDEPATH += ../3rdparty/zlib +} else:!contains(QT_CONFIG, no-zlib) { + unix:LIBS += -lz +# win32:LIBS += libz.lib +} diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp new file mode 100644 index 0000000..3a2dd4a --- /dev/null +++ b/src/network/access/qabstractnetworkcache.cpp @@ -0,0 +1,532 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; +} + +/*! + Returns all the attributes stored with this cache item. + + \sa setAttributes(), QNetworkRequest::Attribute +*/ +QNetworkCacheMetaData::AttributesMap QNetworkCacheMetaData::attributes() const +{ + return d->attributes; +} + +/*! + 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 {Format of the QDataStream operators} +*/ +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 {Format of the QDataStream operators} +*/ +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 0000000..a7eb2b1 --- /dev/null +++ b/src/network/access/qabstractnetworkcache.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..9b6f7d1 --- /dev/null +++ b/src/network/access/qabstractnetworkcache_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..569d2fd --- /dev/null +++ b/src/network/access/qftp.cpp @@ -0,0 +1,2407 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 = new QTcpSocket(this); + 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) +{ + 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 = bytesFromSocket.size(); + memcpy(data, bytesFromSocket.data(), read); + bytesFromSocket.clear(); + } + + 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); + 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 + 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 io + \inmodule QtNetwork + \mainclass + + 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 Trolltech's + 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 QHttp, 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) +{ + d_func()->pi.transferConnectionExtended = true; + QStringList cmds; + cmds << host; + cmds << QString::number((uint)port); + return d_func()->addCommand(new QFtpCommand(ConnectToHost, cmds)); +} + +/*! + 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) +{ + d_func()->pi.transferConnectionExtended = true; + d_func()->transferMode = mode; + return d_func()->addCommand(new QFtpCommand(SetTransferMode, QStringList())); +} + +/*! + 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 QHttp 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) { + 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); + 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 0000000..ba759e0 --- /dev/null +++ b/src/network/access/qftp.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..0141ae2 --- /dev/null +++ b/src/network/access/qhttp.cpp @@ -0,0 +1,3120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_HTTP + +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); + 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); + + 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 + \brief The QHttpHeader class contains header information for HTTP. + + \ingroup io + \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() +{ + delete d_ptr; +} + +/*! + 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 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 + \brief The QHttpResponseHeader class contains response header information for HTTP. + + \ingroup io + \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; +} + +/*! \reimp +*/ +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 + \brief The QHttpRequestHeader class contains request header information for HTTP. + + \ingroup io + \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; +} + +/*! \reimp +*/ +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 + \reentrant + + \brief The QHttp class provides an implementation of the HTTP protocol. + + \ingroup io + \inmodule QtNetwork + \mainclass + + This class provides a direct interface to HTTP that allows you to + have more control over the requests and that allows you to access + the response header fields. 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 "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 Trolltech home page (i.e., the URL + \c http://qtsoftware.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 fopr 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. + + \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://qtsoftware.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://qtsoftware.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://qtsoftware.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 cacheing 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(); +} + +void QHttpPrivate::_q_slotBytesWritten(qint64 written) +{ + Q_Q(QHttp); + bytesDone += written; + emit q->dataSendProgress(bytesDone, bytesTotal); + + if (pendingPost) + return; + + if (!postDevice) + return; + + if (socket->bytesToWrite() == 0) { + 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(const QNetworkProxy &, QAuthenticator *)), + q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *))); +#endif + +#ifndef QT_NO_OPENSSL + if (qobject_cast<QSslSocket *>(socket)) { + QObject::connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), + q, SIGNAL(sslErrors(const QList<QSslError> &))); + } +#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 0000000..771176a --- /dev/null +++ b/src/network/access/qhttp.h @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +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); + 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)) + 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/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp new file mode 100644 index 0000000..edb2988 --- /dev/null +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -0,0 +1,2464 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpnetworkconnection_p.h" +#include <private/qnetworkrequest_p.h> +#include <private/qobject_p.h> +#include <private/qauthenticator_p.h> +#include <qnetworkproxy.h> +#include <qauthenticator.h> +#include <qbytearraymatcher.h> +#include <qbuffer.h> +#include <qpair.h> +#include <qhttp.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_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 + +QT_BEGIN_NAMESPACE + +class QHttpNetworkHeaderPrivate : public QSharedData +{ +public: + QUrl url; + QList<QPair<QByteArray, QByteArray> > fields; + + QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl()); + QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other); + inline qint64 contentLength() const; + inline void setContentLength(qint64 length); + + inline QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const; + inline QList<QByteArray> headerFieldValues(const QByteArray &name) const; + inline void setHeaderField(const QByteArray &name, const QByteArray &data); + bool operator==(const QHttpNetworkHeaderPrivate &other) const; + +}; + +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; + QByteArray value = headerField("content-length"); + 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 (QByteArray value, allValues) { + if (!first) + result += ", "; + first = false; + result += value; + } + return result; +} + +QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const +{ + QList<QByteArray> result; + QByteArray lowerName = name.toLower(); + QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(), + end = fields.constEnd(); + for ( ; it != end; ++it) + if (lowerName == it->first.toLower()) + result += it->second; + + return result; +} + +void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data) +{ + QByteArray lowerName = name.toLower(); + QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(); + while (it != fields.end()) { + if (lowerName == it->first.toLower()) + it = fields.erase(it); + else + ++it; + } + fields.append(qMakePair(name, data)); +} + +bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const +{ + return (url == other.url); +} + +// QHttpNetworkRequestPrivate +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; + QHttpNetworkRequest::Priority priority; + mutable QIODevice *data; + bool autoDecompress; +}; + +QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op, + QHttpNetworkRequest::Priority pri, const QUrl &newUrl) + : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), data(0), + autoDecompress(false) +{ +} + +QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other) + : QHttpNetworkHeaderPrivate(other) +{ + operation = other.operation; + priority = other.priority; + data = other.data; + autoDecompress = other.autoDecompress; +} + +QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate() +{ +} + +bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const +{ + return QHttpNetworkHeaderPrivate::operator==(other) + && (operation == other.operation) + && (data == other.data); +} + +QByteArray QHttpNetworkRequestPrivate::methodName() const +{ + QByteArray ba; + switch (operation) { + case QHttpNetworkRequest::Options: + ba += "OPTIONS"; + break; + case QHttpNetworkRequest::Get: + ba += "GET"; + break; + case QHttpNetworkRequest::Head: + ba += "HEAD"; + break; + case QHttpNetworkRequest::Post: + ba += "POST"; + break; + case QHttpNetworkRequest::Put: + ba += "PUT"; + break; + case QHttpNetworkRequest::Delete: + ba += "DELETE"; + break; + case QHttpNetworkRequest::Trace: + ba += "TRACE"; + break; + case QHttpNetworkRequest::Connect: + ba += "CONNECT"; + break; + default: + break; + } + return ba; +} + +QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const +{ + QUrl::FormattingOptions format(QUrl::RemoveFragment); + + // for POST, query data is send as content + if (operation == QHttpNetworkRequest::Post && !data) + 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) +{ + QByteArray ba = request.d->methodName(); + QByteArray uri = request.d->uri(throughProxy); + ba += " " + uri; + + QString majorVersion = QString::number(request.majorVersion()); + QString minorVersion = QString::number(request.minorVersion()); + ba += " HTTP/" + majorVersion.toLatin1() + "." + minorVersion.toLatin1() + "\r\n"; + + QList<QPair<QByteArray, QByteArray> > fields = request.header(); + QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin(); + for (; it != fields.constEnd(); ++it) + ba += it->first + ": " + it->second + "\r\n"; + if (request.d->operation == QHttpNetworkRequest::Post) { + // add content type, if not set in the request + if (request.headerField("content-type").isEmpty()) + ba += "Content-Type: application/x-www-form-urlencoded\r\n"; + if (!request.d->data && request.d->url.hasQuery()) { + QByteArray query = request.d->url.encodedQuery(); + ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n"; + ba += "\r\n"; + ba += query; + } else { + ba += "\r\n"; + } + } else { + ba += "\r\n"; + } + return ba; +} + +class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate +{ +public: + QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl()); + ~QHttpNetworkReplyPrivate(); + qint64 readStatus(QAbstractSocket *socket); + void parseStatus(const QByteArray &status); + qint64 readHeader(QAbstractSocket *socket); + void parseHeader(const QByteArray &header); + qint64 readBody(QAbstractSocket *socket, QIODevice *out); + bool findChallenge(bool forProxy, QByteArray &challenge) const; + QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const; + void clear(); + + qint64 transferRaw(QIODevice *in, QIODevice *out, qint64 size); + qint64 transferChunked(QIODevice *in, QIODevice *out); + qint64 getChunkSize(QIODevice *in, qint64 *chunkSize); + + qint64 bytesAvailable() const; + bool isChunked(); + bool connectionCloseEnabled(); + 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; + int statusCode; + int majorVersion; + int minorVersion; + QString errorString; + QString reasonPhrase; + qint64 bodyLength; + qint64 contentRead; + qint64 totalProgress; + QByteArray fragment; + qint64 currentChunkSize; + qint64 currentChunkRead; + QPointer<QHttpNetworkConnection> connection; + bool initInflate; + bool streamEnd; +#ifndef QT_NO_COMPRESS + z_stream inflateStrm; +#endif + bool autoDecompress; + + QByteArray responseData; // uncompressed body + QByteArray compressedData; // compressed body (temporary) + QBuffer requestDataBuffer; + bool requestIsBuffering; + bool requestIsPrepared; +}; + +QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) + : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100), + majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0), + currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false), + autoDecompress(false), requestIsBuffering(false), requestIsPrepared(false) +{ +} + +QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate() +{ +} + +void QHttpNetworkReplyPrivate::clear() +{ + state = NothingDoneState; + statusCode = 100; + bodyLength = 0; + contentRead = 0; + totalProgress = 0; + currentChunkSize = 0; + currentChunkRead = 0; + connection = 0; +#ifndef QT_NO_COMPRESS + if (initInflate) + inflateEnd(&inflateStrm); +#endif + initInflate = false; + streamEnd = false; + autoDecompress = false; + fields.clear(); +} + +// QHttpNetworkReplyPrivate +qint64 QHttpNetworkReplyPrivate::bytesAvailable() const +{ + return (state != ReadingDataState ? 0 : fragment.size()); +} + +bool QHttpNetworkReplyPrivate::isGzipped() +{ + QByteArray encoding = headerField("content-encoding"); + return encoding.toLower() == "gzip"; +} + +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"); + QByteArray lowerName = name.toLower(); + QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(), + end = fields.end(); + while (it != end) { + if (name == it->first.toLower()) { + 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); + 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) +{ + qint64 bytes = 0; + char c; + + while (socket->bytesAvailable()) { + // allow both CRLF & LF (only) line endings + if (socket->peek(&c, 1) == 1 && c == '\n') { + bytes += socket->read(&c, 1); // read the "n" + // remove the CR at the end + if (fragment.endsWith('\r')) { + fragment.truncate(fragment.length()-1); + } + parseStatus(fragment); + state = ReadingHeaderState; + fragment.clear(); // next fragment + break; + } else { + c = 0; + bytes += socket->read(&c, 1); + fragment.append(c); + } + } + return bytes; +} + +void QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status) +{ + const QByteArrayMatcher sp(" "); + int i = sp.indexIn(status); + const QByteArray version = status.mid(0, i); + int j = sp.indexIn(status, i + 1); + const QByteArray code = status.mid(i + 1, j - i - 1); + const QByteArray reason = status.mid(j + 1, status.count() - j); + + const QByteArrayMatcher slash("/"); + int k = slash.indexIn(version); + const QByteArrayMatcher dot("."); + int l = dot.indexIn(version, k); + const QByteArray major = version.mid(k + 1, l - k - 1); + const QByteArray minor = version.mid(l + 1, version.count() - l); + + majorVersion = QString::fromAscii(major.constData()).toInt(); + minorVersion = QString::fromAscii(minor.constData()).toInt(); + statusCode = QString::fromAscii(code.constData()).toInt(); + reasonPhrase = QString::fromAscii(reason.constData()); +} + +qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket) +{ + qint64 bytes = 0; + char crlfcrlf[5]; + crlfcrlf[4] = '\0'; + char c = 0; + bool allHeaders = false; + while (!allHeaders && socket->bytesAvailable()) { + if (socket->peek(&c, 1) == 1 && c == '\n') { + // check for possible header endings. As per HTTP rfc, + // the header endings will be marked by CRLFCRLF. But + // we will allow CRLFLF, LFLF & CRLFCRLF + if (fragment.endsWith("\n\r") || fragment.endsWith('\n')) + allHeaders = true; + } + bytes += socket->read(&c, 1); + fragment.append(c); + } + // we received all headers now parse them + if (allHeaders) { + parseHeader(fragment); + state = ReadingDataState; + fragment.clear(); // next fragment + bodyLength = contentLength(); // cache the length + } + 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 headerField("transfer-encoding").toLower().contains("chunked"); +} + +bool QHttpNetworkReplyPrivate::connectionCloseEnabled() +{ + return (headerField("connection").toLower().contains("close") || + headerField("proxy-connection").toLower().contains("close")); +} + +qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out) +{ + qint64 bytes = 0; + if (isChunked()) { + bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6) + } else if (bodyLength > 0) { // we have a Content-Length + bytes += transferRaw(socket, out, bodyLength - contentRead); + if (contentRead + bytes == bodyLength) + state = AllDoneState; + } else { + bytes += transferRaw(socket, out, socket->bytesAvailable()); + } + if (state == AllDoneState) + socket->readAll(); // Read the rest to clean (CRLF) + contentRead += bytes; + return bytes; +} + +qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size) +{ + qint64 bytes = 0; + Q_ASSERT(in); + Q_ASSERT(out); + + int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable())); + QByteArray raw(toBeRead, 0); + while (size > 0) { + qint64 read = in->read(raw.data(), raw.size()); + if (read == 0) + return bytes; + // ### error checking here + qint64 written = out->write(raw.data(), read); + if (written == 0) + return bytes; + if (read != written) + qDebug() << "### read" << read << "written" << written; + bytes += read; + size -= read; + out->waitForBytesWritten(-1); // throttle + } + return bytes; + +} + +qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *out) +{ + qint64 bytes = 0; + while (in->bytesAvailable()) { // while we can read from input + // if we are done with the current chunk, get the size of the new chunk + if (currentChunkRead >= currentChunkSize) { + currentChunkSize = 0; + currentChunkRead = 0; + if (bytes) { + char crlf[2]; + bytes += in->read(crlf, 2); // read the "\r\n" after the chunk + } + bytes += getChunkSize(in, ¤tChunkSize); + if (currentChunkSize == -1) + break; + } + // if the chunk size is 0, end of the stream + if (currentChunkSize == 0) { + state = AllDoneState; + break; + } + // otherwise, read data + qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead); + QByteArray buffer(readSize, 0); + qint64 read = in->read(buffer.data(), readSize); + bytes += read; + currentChunkRead += read; + qint64 written = out->write(buffer); + Q_UNUSED(written); // Avoid compile warning when building release + Q_ASSERT(read == written); + // ### error checking here + out->waitForBytesWritten(-1); + } + return bytes; +} + +qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize) +{ + qint64 bytes = 0; + char crlf[2]; + *chunkSize = -1; + int bytesAvailable = in->bytesAvailable(); + while (bytesAvailable > bytes) { + qint64 sniffedBytes = in->peek(crlf, 2); + int fragmentSize = fragment.size(); + // check the next two bytes for a "\r\n", skip blank lines + if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n') + ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n')) + { + bytes += in->read(crlf, 1); // read the \r or \n + if (crlf[0] == '\r') + bytes += in->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; + bytes += in->read(&c, 1); + fragment.append(c); + } + } + return bytes; +} + +// QHttpNetworkConnectionPrivate + +typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair; + +class QHttpNetworkConnectionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QHttpNetworkConnection) +public: + QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); + ~QHttpNetworkConnectionPrivate(); + void init(); + void connectSignals(QAbstractSocket *socket); + + enum SocketState { + 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 + Wait4AuthState = 0x10, // blocked for send till the current authentication slot is done + BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState) + }; + + enum { ChunkSize = 4096 }; + + int indexOf(QAbstractSocket *socket) const; + bool isSocketBusy(QAbstractSocket *socket) const; + bool isSocketWriting(QAbstractSocket *socket) const; + bool isSocketWaiting(QAbstractSocket *socket) const; + bool isSocketReading(QAbstractSocket *socket) const; + + QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request); + void unqueueRequest(QAbstractSocket *socket); + void prepareRequest(HttpMessagePair &request); + bool sendRequest(QAbstractSocket *socket); + void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply); + void resendCurrentRequest(QAbstractSocket *socket); + void closeChannel(int channel); + void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); + + // private slots + void _q_bytesWritten(qint64 bytes); // proceed sending + void _q_readyRead(); // pending data to read + void _q_disconnected(); // disconnected from host + void _q_startNextRequest(); // send the next request from the queue + void _q_restartPendingRequest(); // send the currently blocked request + 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_dataReadyReadNoBuffer(); + void _q_dataReadyReadBuffer(); + + void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); + bool ensureConnection(QAbstractSocket *socket); + QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket); + void eraseData(QHttpNetworkReply *reply); +#ifndef QT_NO_COMPRESS + bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete); +#endif + void bufferData(HttpMessagePair &request); + void removeReply(QHttpNetworkReply *reply); + + QString hostName; + quint16 port; + bool encrypt; + + struct Channel { + QAbstractSocket *socket; + SocketState 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 authMehtod; + QAuthenticatorPrivate::Method proxyAuthMehtod; + QAuthenticator authenticator; + QAuthenticator proxyAuthenticator; +#ifndef QT_NO_OPENSSL + bool ignoreSSLErrors; +#endif + Channel() :state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), reconnectAttempts(2), + authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None) +#ifndef QT_NO_OPENSSL + , ignoreSSLErrors(false) +#endif + {} + }; + static const int channelCount; + Channel channels[2]; // maximum of 2 socket connections to the server + bool pendingAuthSignal; // there is an incomplete authentication signal + bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal + + void appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed); + qint64 bytesAvailable(const QHttpNetworkReply &reply, bool compressed = false) const; + qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed); + void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode); + bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend); + void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply); + void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply); + inline bool emitSignals(QHttpNetworkReply *reply); + inline bool expectContent(QHttpNetworkReply *reply); + +#ifndef QT_NO_OPENSSL + void _q_encrypted(); // start sending request (https) + void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket + QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const; +#endif + +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy networkProxy; +#endif + + //The request queues + QList<HttpMessagePair> highPriorityQueue; + QList<HttpMessagePair> lowPriorityQueue; +}; + +const int QHttpNetworkConnectionPrivate::channelCount = 2; + +QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt) +: hostName(hostName), port(port), encrypt(encrypt), + pendingAuthSignal(false), pendingProxyAuthSignal(false) +#ifndef QT_NO_NETWORKPROXY + , networkProxy(QNetworkProxy::NoProxy) +#endif +{ +} + +QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() +{ + for (int i = 0; i < channelCount; ++i) { + channels[i].socket->close(); + delete channels[i].socket; + } +} + +void QHttpNetworkConnectionPrivate::connectSignals(QAbstractSocket *socket) +{ + Q_Q(QHttpNetworkConnection); + + QObject::connect(socket, SIGNAL(bytesWritten(qint64)), + q, SLOT(_q_bytesWritten(qint64)), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(connected()), + q, SLOT(_q_connected()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(readyRead()), + q, SLOT(_q_readyRead()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(disconnected()), + q, SLOT(_q_disconnected()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + q, SLOT(_q_error(QAbstractSocket::SocketError)), + Qt::DirectConnection); +#ifndef QT_NO_NETWORKPROXY + QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + q, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + Qt::DirectConnection); +#endif + +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + QObject::connect(sslSocket, SIGNAL(encrypted()), + q, SLOT(_q_encrypted()), + Qt::DirectConnection); + QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), + q, SLOT(_q_sslErrors(const QList<QSslError>&)), + Qt::DirectConnection); +#endif +} + +void QHttpNetworkConnectionPrivate::init() +{ + for (int i = 0; i < channelCount; ++i) { +#ifndef QT_NO_OPENSSL + channels[i].socket = new QSslSocket; +#else + channels[i].socket = new QTcpSocket; +#endif + connectSignals(channels[i].socket); + } +} + +int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const +{ + for (int i = 0; i < channelCount; ++i) + if (channels[i].socket == socket) + return i; + return -1; +} + +bool QHttpNetworkConnectionPrivate::isSocketBusy(QAbstractSocket *socket) const +{ + int i = indexOf(socket); + return (channels[i].state & BusyState); +} + +bool QHttpNetworkConnectionPrivate::isSocketWriting(QAbstractSocket *socket) const +{ + int i = indexOf(socket); + return (i != -1 && (channels[i].state & WritingState)); +} + +bool QHttpNetworkConnectionPrivate::isSocketWaiting(QAbstractSocket *socket) const +{ + int i = indexOf(socket); + return (i != -1 && (channels[i].state & WaitingState)); +} + +bool QHttpNetworkConnectionPrivate::isSocketReading(QAbstractSocket *socket) const +{ + int i = indexOf(socket); + return (i != -1 && (channels[i].state & ReadingState)); +} + + +void QHttpNetworkConnectionPrivate::appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed) +{ + QByteArray *ba = (compressed) ? &reply.d_func()->compressedData : &reply.d_func()->responseData; + ba->append(fragment); + return; +} + +qint64 QHttpNetworkConnectionPrivate::bytesAvailable(const QHttpNetworkReply &reply, bool compressed) const +{ + const QByteArray *ba = (compressed) ? &reply.d_func()->compressedData : &reply.d_func()->responseData; + return ba->size(); +} + +qint64 QHttpNetworkConnectionPrivate::read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed) +{ + QByteArray *ba = (compressed) ? &reply.d_func()->compressedData : &reply.d_func()->responseData; + if (maxSize == -1 || maxSize >= ba->size()) { + // read the whole data + data = *ba; + ba->clear(); + } else { + // read only the requested length + data = ba->mid(0, maxSize); + ba->remove(0, maxSize); + } + return data.size(); +} + +void QHttpNetworkConnectionPrivate::eraseData(QHttpNetworkReply *reply) +{ + reply->d_func()->compressedData.clear(); + reply->d_func()->responseData.clear(); +} + +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 + QIODevice *data = request.data(); + if (data && request.contentLength() == -1) { + if (!data->isSequential()) + request.setContentLength(data->size()); + else + bufferData(messagePair); // ### or do chunked upload + } + // 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 + // set the gzip header + 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 + } + // 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; +} + +bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket) +{ + // make sure that this socket is in a connected state, if not initiate + // connection to the host. + if (socket->state() != QAbstractSocket::ConnectedState) { + // connect to the host if not already connected. + int index = indexOf(socket); + channels[index].state = ConnectingState; + channels[index].pendingEncrypt = encrypt; + + // 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(channels[index].authenticator); + if (priv && priv->phase == QAuthenticatorPrivate::Done) + priv->phase = QAuthenticatorPrivate::Start; + priv = QAuthenticatorPrivate::getPrivate(channels[index].proxyAuthenticator); + if (priv && priv->phase == QAuthenticatorPrivate::Done) + priv->phase = QAuthenticatorPrivate::Start; + + QString connectHost = hostName; + qint16 connectPort = port; + +#ifndef QT_NO_NETWORKPROXY + // HTTPS always use transparent proxy. + if (networkProxy.type() != QNetworkProxy::NoProxy && !encrypt) { + connectHost = networkProxy.hostName(); + connectPort = networkProxy.port(); + } +#endif + if (encrypt) { +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + sslSocket->connectToHostEncrypted(connectHost, connectPort); + if (channels[index].ignoreSSLErrors) + sslSocket->ignoreSslErrors(); +#else + emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError); +#endif + } else { + socket->connectToHost(connectHost, connectPort); + } + return false; + } + return true; +} + + +bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) +{ + Q_Q(QHttpNetworkConnection); + + int i = indexOf(socket); + switch (channels[i].state) { + case IdleState: { // write the header + if (!ensureConnection(socket)) { + // wait for the connection (and encryption) to be done + // sendRequest will be called again from either + // _q_connected or _q_encrypted + return false; + } + channels[i].written = 0; // excluding the header + channels[i].bytesTotal = 0; + if (channels[i].reply) { + channels[i].reply->d_func()->clear(); + channels[i].reply->d_func()->connection = q; + channels[i].reply->d_func()->autoDecompress = channels[i].request.d->autoDecompress; + } + channels[i].state = WritingState; + channels[i].pendingEncrypt = false; + // if the url contains authentication parameters, use the new ones + // both channels will use the new authentication parameters + if (!channels[i].request.url().userInfo().isEmpty()) { + QUrl url = channels[i].request.url(); + QAuthenticator &auth = channels[i].authenticator; + if (url.userName() != auth.user() + || (!url.password().isEmpty() && url.password() != auth.password())) { + auth.setUser(url.userName()); + auth.setPassword(url.password()); + copyCredentials(i, &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()); + channels[i].request.setUrl(url); + } + createAuthorization(socket, channels[i].request); +#ifndef QT_NO_NETWORKPROXY + QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, + (networkProxy.type() != QNetworkProxy::NoProxy)); +#else + QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request, + false); +#endif + socket->write(header); + QIODevice *data = channels[i].request.d->data; + QHttpNetworkReply *reply = channels[i].reply; + if (reply && reply->d_func()->requestDataBuffer.size()) + data = &channels[i].reply->d_func()->requestDataBuffer; + if (data && (data->isOpen() || data->open(QIODevice::ReadOnly))) { + if (data->isSequential()) { + channels[i].bytesTotal = -1; + QObject::connect(data, SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadNoBuffer())); + QObject::connect(data, SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadNoBuffer())); + } else { + channels[i].bytesTotal = data->size(); + } + } else { + channels[i].state = WaitingState; + break; + } + // write the initial chunk together with the headers + // fall through + } + case WritingState: { // write the data + QIODevice *data = channels[i].request.d->data; + if (channels[i].reply->d_func()->requestDataBuffer.size()) + data = &channels[i].reply->d_func()->requestDataBuffer; + if (!data || channels[i].bytesTotal == channels[i].written) { + channels[i].state = WaitingState; // now wait for response + break; + } + + QByteArray chunk; + chunk.resize(ChunkSize); + qint64 readSize = data->read(chunk.data(), ChunkSize); + if (readSize == -1) { + // source has reached EOF + channels[i].state = WaitingState; // now wait for response + } else if (readSize > 0) { + // source gave us something useful + channels[i].written += socket->write(chunk.data(), readSize); + if (channels[i].reply) + emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal); + } + break; + } + case WaitingState: + case ReadingState: + case Wait4AuthState: + // ignore _q_bytesWritten in these states + // fall through + default: + break; + } + return true; +} + +bool QHttpNetworkConnectionPrivate::emitSignals(QHttpNetworkReply *reply) +{ + // for 401 & 407 don't emit the data signals. Content along with these + // responses are send only if the authentication fails. + return (reply && reply->d_func()->statusCode != 401 && reply->d_func()->statusCode != 407); +} + +bool QHttpNetworkConnectionPrivate::expectContent(QHttpNetworkReply *reply) +{ + // check whether we can expect content after the headers (rfc 2616, sec4.4) + if (!reply) + return false; + if ((reply->d_func()->statusCode >= 100 && reply->d_func()->statusCode < 200) + || reply->d_func()->statusCode == 204 || reply->d_func()->statusCode == 304) + return false; + if (reply->d_func()->request.operation() == QHttpNetworkRequest::Head) + return !emitSignals(reply); + if (reply->d_func()->contentLength() == 0) + return false; + return 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 + eraseData(channels[i].reply); + closeChannel(i); + // send the next request + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + } +} + +#ifndef QT_NO_COMPRESS +bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete) +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + qint64 total = bytesAvailable(*reply, true); + if (total >= CHUNK || dataComplete) { + int i = indexOf(socket); + // uncompress the data + QByteArray content, inflated; + read(*reply, content, -1, true); + 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(); + appendData(*reply, inflated, false); + if (emitSignals(reply)) { + emit reply->readyRead(); + // make sure that the reply is valid + if (channels[i].reply != reply) + return true; + emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); + // make sure that the reply is valid + if (channels[i].reply != reply) + return true; + + } + } + } else { + emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); + return false; + } + } + return true; +} +#endif + +void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply) +{ + Q_ASSERT(socket); + + Q_Q(QHttpNetworkConnection); + qint64 bytes = 0; + QAbstractSocket::SocketState state = socket->state(); + int i = indexOf(socket); + + // connection might be closed to signal the end of data + if (state == QAbstractSocket::UnconnectedState) { + if (!socket->bytesAvailable()) { + if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; + channels[i].state = IdleState; + allDone(socket, reply); + } else { + // try to reconnect/resend before sending an error. + if (channels[i].reconnectAttempts-- > 0) { + resendCurrentRequest(socket); + } else { + reply->d_func()->errorString = errorDetail(QNetworkReply::RemoteHostClosedError, socket); + emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString); + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + } + } + } + } + + // read loop for the response + while (socket->bytesAvailable()) { + QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState; + switch (state) { + case QHttpNetworkReplyPrivate::NothingDoneState: + case QHttpNetworkReplyPrivate::ReadingStatusState: + bytes += reply->d_func()->readStatus(socket); + channels[i].lastStatus = reply->d_func()->statusCode; + break; + case QHttpNetworkReplyPrivate::ReadingHeaderState: + bytes += reply->d_func()->readHeader(socket); + if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) { + if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) { + // remove the Content-Length from header + reply->d_func()->removeAutoDecompressHeader(); + } else { + reply->d_func()->autoDecompress = false; + } + if (reply && reply->d_func()->statusCode == 100) { + reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState; + break; // ignore + } + if (emitSignals(reply)) + emit reply->headerChanged(); + if (!expectContent(reply)) { + reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState; + channels[i].state = IdleState; + allDone(socket, reply); + return; + } + } + break; + case QHttpNetworkReplyPrivate::ReadingDataState: { + QBuffer fragment; + fragment.open(QIODevice::WriteOnly); + bytes = reply->d_func()->readBody(socket, &fragment); + if (bytes) { + appendData(*reply, fragment.data(), reply->d_func()->autoDecompress); + if (!reply->d_func()->autoDecompress) { + reply->d_func()->totalProgress += fragment.size(); + if (emitSignals(reply)) { + emit reply->readyRead(); + // make sure that the reply is valid + if (channels[i].reply != reply) + return; + emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength); + // make sure that the reply is valid + if (channels[i].reply != reply) + return; + } + } +#ifndef QT_NO_COMPRESS + else if (!expand(socket, reply, false)) { // expand a chunk if possible + return; // ### expand failed + } +#endif + } + if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) + break; + // everything done, fall through + } + case QHttpNetworkReplyPrivate::AllDoneState: + channels[i].state = IdleState; + allDone(socket, reply); + break; + default: + break; + } + } +} + +void QHttpNetworkConnectionPrivate::allDone(QAbstractSocket *socket, QHttpNetworkReply *reply) +{ +#ifndef QT_NO_COMPRESS + // expand the whole data. + if (expectContent(reply) && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) + expand(socket, reply, true); // ### if expand returns false, its an error +#endif + // while handling 401 & 407, we might reset the status code, so save this. + bool emitFinished = emitSignals(reply); + handleStatus(socket, reply); + // ### at this point there should be no more data on the socket + // close if server requested + int i = indexOf(socket); + if (reply->d_func()->connectionCloseEnabled()) + closeChannel(i); + // queue the finished signal, this is required since we might send new requests from + // slot connected to it. The socket will not fire readyRead signal, if we are already + // in the slot connected to readyRead + if (emitFinished) + 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. + channels[i].reconnectAttempts = 2; +} + +void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply) +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + Q_Q(QHttpNetworkConnection); + + int statusCode = reply->statusCode(); + bool resend = false; + + switch (statusCode) { + case 401: + case 407: + handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend); + if (resend) { + eraseData(reply); + sendRequest(socket); + } + break; + default: + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + } +} + +void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy) +{ + Q_ASSERT(auth); + + // 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()); + } +} + + +bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, + bool isProxy, bool &resend) +{ + Q_ASSERT(socket); + Q_ASSERT(reply); + + Q_Q(QHttpNetworkConnection); + + resend = false; + //create the response header to be used with QAuthenticatorPrivate. + QHttpResponseHeader responseHeader; + QList<QPair<QByteArray, QByteArray> > fields = reply->header(); + QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin(); + while (it != fields.constEnd()) { + responseHeader.addValue(QString::fromLatin1(it->first), QString::fromUtf8(it->second)); + it++; + } + //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].proxyAuthMehtod = authMethod; + } else { + auth = &channels[i].authenticator; + channels[i].authMehtod = authMethod; + } + //proceed with the authentication. + if (auth->isNull()) + auth->detach(); + QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth); + priv->parseHttpResponse(responseHeader, isProxy); + + if (priv->phase == QAuthenticatorPrivate::Done) { + if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) { + // drop the request + eraseData(channels[i].reply); + closeChannel(i); + channels[i].lastStatus = 0; + channels[i].state = Wait4AuthState; + return false; + } + // cannot use this socket until the slot returns + channels[i].state = WaitingState; + socket->blockSignals(true); + if (!isProxy) { + pendingAuthSignal = true; + emit q->authenticationRequired(reply->request(), auth, q); + pendingAuthSignal = false; +#ifndef QT_NO_NETWORKPROXY + } else { + pendingProxyAuthSignal = true; + emit q->proxyAuthenticationRequired(networkProxy, auth, q); + pendingProxyAuthSignal = false; +#endif + } + socket->blockSignals(false); + // socket free to use + channels[i].state = IdleState; + if (priv->phase != QAuthenticatorPrivate::Done) { + // send any pending requests + copyCredentials(i, auth, isProxy); + QMetaObject::invokeMethod(q, "_q_restartPendingRequest", Qt::QueuedConnection); + } + } + // changing values in QAuthenticator will reset the 'phase' + if (priv->phase == QAuthenticatorPrivate::Done) { + // authentication is cancelled, send the current contents to the user. + emit channels[i].reply->headerChanged(); + emit channels[i].reply->readyRead(); + emit channels[i].reply->finished(); + QNetworkReply::NetworkError errorCode = + isProxy + ? QNetworkReply::ProxyAuthenticationRequiredError + : QNetworkReply::AuthenticationRequiredError; + reply->d_func()->errorString = errorDetail(errorCode, socket); + emit q->error(errorCode, reply->d_func()->errorString); + emit channels[i].reply->finished(); + // ### at this point the reply could be deleted + socket->close(); + // remove pending request on the other channels + for (int j = 0; j < channelCount; ++j) { + if (j != i && channels[j].state == Wait4AuthState) + channels[j].state = IdleState; + } + 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); + if (channels[i].authMehtod != QAuthenticatorPrivate::None) { + if (!(channels[i].authMehtod == 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); + } + } + } + if (channels[i].proxyAuthMehtod != QAuthenticatorPrivate::None) { + if (!(channels[i].proxyAuthMehtod == 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; + 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; + } + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); + return reply; +} + +void QHttpNetworkConnectionPrivate::unqueueRequest(QAbstractSocket *socket) +{ + Q_ASSERT(socket); + + int i = indexOf(socket); + + if (!highPriorityQueue.isEmpty()) { + for (int j = highPriorityQueue.count() - 1; j >= 0; --j) { + HttpMessagePair &messagePair = highPriorityQueue[j]; + if (!messagePair.second->d_func()->requestIsPrepared) + prepareRequest(messagePair); + if (!messagePair.second->d_func()->requestIsBuffering) { + channels[i].request = messagePair.first; + channels[i].reply = messagePair.second; + sendRequest(socket); + highPriorityQueue.removeAt(j); + return; + } + } + } + + if (!lowPriorityQueue.isEmpty()) { + for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) { + HttpMessagePair &messagePair = lowPriorityQueue[j]; + if (!messagePair.second->d_func()->requestIsPrepared) + prepareRequest(messagePair); + if (!messagePair.second->d_func()->requestIsBuffering) { + channels[i].request = messagePair.first; + channels[i].reply = messagePair.second; + sendRequest(socket); + lowPriorityQueue.removeAt(j); + return; + } + } + } +} + +void QHttpNetworkConnectionPrivate::closeChannel(int channel) +{ + QAbstractSocket *socket = channels[channel].socket; + socket->blockSignals(true); + socket->close(); + socket->blockSignals(false); + channels[channel].state = IdleState; +} + +void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) +{ + Q_Q(QHttpNetworkConnection); + Q_ASSERT(socket); + int i = indexOf(socket); + closeChannel(i); + channels[i].resendCurrent = true; + QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); +} + +QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket) +{ + 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("QHttp", "HTTP request failed")); + 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 = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTP request failed")); + break; + } + return errorString; +} + +void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) +{ + Q_Q(QHttpNetworkConnection); + + // remove the from active list. + for (int i = 0; i < channelCount; ++i) { + if (channels[i].reply == reply) { + channels[i].reply = 0; + closeChannel(i); + 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; + } + } + } +} + + +//private slots +void QHttpNetworkConnectionPrivate::_q_readyRead() +{ + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; // ### error + if (isSocketWaiting(socket) || isSocketReading(socket)) { + int i = indexOf(socket); + channels[i].state = ReadingState; + if (channels[i].reply) + receiveReply(socket, channels[i].reply); + } + // ### error +} + +void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes) +{ + Q_UNUSED(bytes); + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; // ### error + if (isSocketWriting(socket)) + sendRequest(socket); + // otherwise we do nothing +} + +void QHttpNetworkConnectionPrivate::_q_disconnected() +{ + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; // ### error + // read the available data before closing + int i = indexOf(socket); + if (isSocketWaiting(socket) || isSocketReading(socket)) { + channels[i].state = ReadingState; + if (channels[i].reply) + receiveReply(socket, channels[i].reply); + } + channels[i].state = IdleState; +} + +void QHttpNetworkConnectionPrivate::_q_startNextRequest() +{ + // send the current request again + if (channels[0].resendCurrent || channels[1].resendCurrent) { + int i = channels[0].resendCurrent ? 0:1; + QAbstractSocket *socket = channels[i].socket; + channels[i].resendCurrent = false; + channels[i].state = IdleState; + if (channels[i].reply) + sendRequest(socket); + return; + } + // send the request using the idle socket + QAbstractSocket *socket = channels[0].socket; + if (isSocketBusy(socket)) { + socket = (isSocketBusy(channels[1].socket) ? 0 :channels[1].socket); + } + + if (!socket) { + return; // this will be called after finishing current request. + } + unqueueRequest(socket); +} + +void QHttpNetworkConnectionPrivate::_q_restartPendingRequest() +{ + // send the request using the idle socket + for (int i = 0 ; i < channelCount; ++i) { + QAbstractSocket *socket = channels[i].socket; + if (channels[i].state == Wait4AuthState) { + channels[i].state = IdleState; + if (channels[i].reply) + sendRequest(socket); + } + } +} + +void QHttpNetworkConnectionPrivate::_q_connected() +{ + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; // ### error + int i = indexOf(socket); + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! + //channels[i].reconnectAttempts = 2; + if (!channels[i].pendingEncrypt) { + channels[i].state = IdleState; + if (channels[i].reply) + sendRequest(socket); + else + closeChannel(i); + } +} + + +void QHttpNetworkConnectionPrivate::_q_error(QAbstractSocket::SocketError socketError) +{ + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; + bool send2Reply = false; + int i = indexOf(socket); + 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 (channels[i].state != IdleState && channels[i].state != ReadingState) { + if (channels[i].reconnectAttempts-- > 0) { + resendCurrentRequest(socket); + return; + } else { + send2Reply = true; + errorCode = QNetworkReply::RemoteHostClosedError; + } + } else { + return; + } + break; + case QAbstractSocket::SocketTimeoutError: + // try to reconnect/resend before sending an error. + if (channels[i].state == WritingState && (channels[i].reconnectAttempts-- > 0)) { + resendCurrentRequest(socket); + return; + } + send2Reply = true; + 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<QObject> that = q; + QString errorString = errorDetail(errorCode, socket); + if (send2Reply) { + if (channels[i].reply) { + channels[i].reply->d_func()->errorString = errorString; + // this error matters only to this reply + emit channels[i].reply->finishedWithError(errorCode, errorString); + } + // send the next request + QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); + } else { + // the failure affects all requests. + emit q->error(errorCode, errorString); + } + if (that) //signals make enter the event loop + closeChannel(i); +} + +#ifndef QT_NO_NETWORKPROXY +void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) +{ + Q_Q(QHttpNetworkConnection); + emit q->proxyAuthenticationRequired(proxy, auth, q); +} +#endif + +void QHttpNetworkConnectionPrivate::_q_dataReadyReadNoBuffer() +{ + Q_Q(QHttpNetworkConnection); + // data emitted either readyRead() + // find out which channel it is for + QIODevice *sender = qobject_cast<QIODevice *>(q->sender()); + + // won't match anything if the qobject_cast above failed + for (int i = 0; i < channelCount; ++i) { + if (sender == channels[i].request.data()) { + sendRequest(channels[i].socket); + break; + } + } +} + +void QHttpNetworkConnectionPrivate::_q_dataReadyReadBuffer() +{ + Q_Q(QHttpNetworkConnection); + QIODevice *sender = qobject_cast<QIODevice *>(q->sender()); + HttpMessagePair *thePair = 0; + for (int i = 0; !thePair && i < lowPriorityQueue.size(); ++i) + if (lowPriorityQueue.at(i).first.data() == sender) + thePair = &lowPriorityQueue[i]; + + for (int i = 0; !thePair && i < highPriorityQueue.size(); ++i) + if (highPriorityQueue.at(i).first.data() == sender) + thePair = &highPriorityQueue[i]; + + if (thePair) { + bufferData(*thePair); + + // are we finished buffering? + if (!thePair->second->d_func()->requestIsBuffering) + _q_startNextRequest(); + } +} + +void QHttpNetworkConnectionPrivate::bufferData(HttpMessagePair &messagePair) +{ + Q_Q(QHttpNetworkConnection); + QHttpNetworkRequest &request = messagePair.first; + QHttpNetworkReply *reply = messagePair.second; + Q_ASSERT(request.data()); + if (!reply->d_func()->requestIsBuffering) { // first time + QObject::connect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer())); + QObject::connect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer())); + reply->d_func()->requestIsBuffering = true; + reply->d_func()->requestDataBuffer.open(QIODevice::ReadWrite); + } + + // always try to read at least one byte + // ### FIXME! use a QRingBuffer + qint64 bytesToRead = qMax<qint64>(1, request.data()->bytesAvailable()); + QByteArray newData; + newData.resize(bytesToRead); + qint64 bytesActuallyRead = request.data()->read(newData.data(), bytesToRead); + + if (bytesActuallyRead > 0) { + // we read something + newData.chop(bytesToRead - bytesActuallyRead); + reply->d_func()->requestDataBuffer.write(newData); + } else if (bytesActuallyRead == -1) { // last time + QObject::disconnect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer())); + QObject::disconnect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer())); + + request.setContentLength(reply->d_func()->requestDataBuffer.size()); + reply->d_func()->requestDataBuffer.seek(0); + reply->d_func()->requestIsBuffering = false; + } +} + +// QHttpNetworkConnection + +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() +{ +} + +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); +} + +void QHttpNetworkConnection::enableEncryption() +{ + Q_D(QHttpNetworkConnection); + d->encrypt = true; +} + +bool QHttpNetworkConnection::isEncrypted() const +{ + Q_D(const QHttpNetworkConnection); + return d->encrypt; +} + +void QHttpNetworkConnection::setProxyAuthentication(QAuthenticator *authenticator) +{ + Q_D(QHttpNetworkConnection); + for (int i = 0; i < d->channelCount; ++i) + d->channels[i].proxyAuthenticator = *authenticator; +} + +void QHttpNetworkConnection::setAuthentication(const QString &domain, QAuthenticator *authenticator) +{ + Q_UNUSED(domain); // ### domain ? + Q_D(QHttpNetworkConnection); + for (int i = 0; i < d->channelCount; ++i) + d->channels[i].authenticator = *authenticator; +} + +#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 + +// 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; +} + +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; +} + +QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const +{ + return d->priority; +} + +void QHttpNetworkRequest::setPriority(Priority priority) +{ + d->priority = priority; +} + +QIODevice *QHttpNetworkRequest::data() const +{ + return d->data; +} + +void QHttpNetworkRequest::setData(QIODevice *data) +{ + d->data = data; +} + +int QHttpNetworkRequest::majorVersion() const +{ + return 1; +} + +int QHttpNetworkRequest::minorVersion() const +{ + return 1; +} + +// QHttpNetworkReply + +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; +} + +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()->bytesAvailable(*this); + else + return -1; +} + +QByteArray QHttpNetworkReply::read(qint64 maxSize) +{ + Q_D(QHttpNetworkReply); + QByteArray data; + if (d->connection) + d->connection->d_func()->read(*this, data, maxSize, false); + return data; +} + +bool QHttpNetworkReply::isFinished() const +{ + return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState; +} + +// SSL support below +#ifndef QT_NO_OPENSSL +void QHttpNetworkConnectionPrivate::_q_encrypted() +{ + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; // ### error + channels[indexOf(socket)].state = IdleState; + sendRequest(socket); +} + +void QHttpNetworkConnectionPrivate::_q_sslErrors(const QList<QSslError> &errors) +{ + Q_Q(QHttpNetworkConnection); + QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); + if (!socket) + return; + //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; + emit q->sslErrors(errors); +} + +QSslConfiguration QHttpNetworkConnectionPrivate::sslConfiguration(const QHttpNetworkReply &reply) const +{ + for (int i = 0; i < channelCount; ++i) + if (channels[i].reply == &reply) + return static_cast<QSslSocket *>(channels[0].socket)->sslConfiguration(); + return QSslConfiguration(); // pending or done request +} + +void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config) +{ + Q_D(QHttpNetworkConnection); + // 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 (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].ignoreSSLErrors = true; + } + + } else { + static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(); + d->channels[channel].ignoreSSLErrors = true; + } +} + +QSslConfiguration QHttpNetworkReply::sslConfiguration() const +{ + Q_D(const QHttpNetworkReply); + if (d->connection) + return d->connection->d_func()->sslConfiguration(*this); + return QSslConfiguration(); +} + +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(); +} +#endif //QT_NO_OPENSSL + + +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 0000000..8dbcb3d --- /dev/null +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +#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 QHttpNetworkConnectionPrivate; +class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject +{ + Q_OBJECT +public: + + QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0); + ~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 + + //enable encryption + void enableEncryption(); + bool isEncrypted() const; + + //authentication parameters + void setProxyAuthentication(QAuthenticator *authenticator); + void setAuthentication(const QString &domain, QAuthenticator *authenticator); + +#ifndef QT_NO_OPENSSL + void setSslConfiguration(const QSslConfiguration &config); + void ignoreSslErrors(int channel = -1); + +Q_SIGNALS: + void sslErrors(const QList<QSslError> &errors); +#endif + +Q_SIGNALS: +#ifndef QT_NO_NETWORKPROXY + //cannot be used with queued connection. + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator, + const QHttpNetworkConnection *connection = 0); +#endif + void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *authenticator, + const QHttpNetworkConnection *connection = 0); + void error(QNetworkReply::NetworkError errorCode, const QString &detail = QString()); + +private: + Q_DECLARE_PRIVATE(QHttpNetworkConnection) + Q_DISABLE_COPY(QHttpNetworkConnection) + friend class QHttpNetworkReply; + + Q_PRIVATE_SLOT(d_func(), void _q_bytesWritten(qint64)) + Q_PRIVATE_SLOT(d_func(), void _q_readyRead()) + Q_PRIVATE_SLOT(d_func(), void _q_disconnected()) + Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest()) + Q_PRIVATE_SLOT(d_func(), void _q_restartPendingRequest()) + Q_PRIVATE_SLOT(d_func(), void _q_connected()) + Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError)) +#ifndef QT_NO_NETWORKPROXY + Q_PRIVATE_SLOT(d_func(), void _q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)) +#endif + Q_PRIVATE_SLOT(d_func(), void _q_dataReadyReadBuffer()) + Q_PRIVATE_SLOT(d_func(), void _q_dataReadyReadNoBuffer()) + +#ifndef QT_NO_OPENSSL + Q_PRIVATE_SLOT(d_func(), void _q_encrypted()) + Q_PRIVATE_SLOT(d_func(), void _q_sslErrors(const QList<QSslError>&)) +#endif +}; + +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 QHttpNetworkRequestPrivate; +class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader +{ +public: + enum Operation { + Options, + Get, + Head, + Post, + Put, + Delete, + Trace, + Connect + }; + + 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); + + Priority priority() const; + void setPriority(Priority priority); + + QIODevice *data() const; + void setData(QIODevice *data); + +private: + QSharedDataPointer<QHttpNetworkRequestPrivate> d; + friend class QHttpNetworkRequestPrivate; + friend 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; + QByteArray read(qint64 maxSize = -1); + + bool isFinished() const; + +#ifndef QT_NO_OPENSSL + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration &config); + void ignoreSslErrors(); + +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(); + void dataReadProgress(int done, int total); + void dataSendProgress(int done, int total); + +private: + Q_DECLARE_PRIVATE(QHttpNetworkReply) + friend class QHttpNetworkConnection; + friend class QHttpNetworkConnectionPrivate; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QHttpNetworkRequest) +//Q_DECLARE_METATYPE(QHttpNetworkReply) + +#endif // QT_NO_HTTP + +#endif diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp new file mode 100644 index 0000000..df468b8 --- /dev/null +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "qnetworkaccesscachebackend_p.h" +#include "qabstractnetworkcache.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()->prepend(this); +} + +QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() +{ + if (!factoryDataShutdown) { + QMutexLocker locker(&factoryData()->mutex); + factoryData()->removeAll(this); + } +} + +QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op, + const QNetworkRequest &request) +{ + QNetworkRequest::CacheLoadControl mode = + static_cast<QNetworkRequest::CacheLoadControl>( + request.attribute(QNetworkRequest::CacheLoadControlAttribute, + QNetworkRequest::PreferNetwork).toInt()); + if (mode == QNetworkRequest::AlwaysCache + && (op == QNetworkAccessManager::GetOperation + || op == QNetworkAccessManager::HeadOperation)) + return new QNetworkAccessCacheBackend; + + 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; +} + +QNetworkAccessBackend::QNetworkAccessBackend() +{ +} + +QNetworkAccessBackend::~QNetworkAccessBackend() +{ +} + +void QNetworkAccessBackend::upstreamReadyRead() +{ + // do nothing +} + +void QNetworkAccessBackend::downstreamReadyWrite() +{ + // do nothing +} + +void QNetworkAccessBackend::copyFinished(QIODevice *) +{ + // do nothing +} + +void QNetworkAccessBackend::ignoreSslErrors() +{ + // 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 +{ + return reply->networkCache; // should be the same as manager->networkCache +} + +void QNetworkAccessBackend::setCachingEnabled(bool enable) +{ + reply->setCachingEnabled(enable); +} + +bool QNetworkAccessBackend::isCachingEnabled() const +{ + return reply->isCachingEnabled(); +} + +qint64 QNetworkAccessBackend::upstreamBytesAvailable() const +{ + return reply->writeBuffer.size(); +} + +void QNetworkAccessBackend::upstreamBytesConsumed(qint64 count) +{ + // remove count bytes from the write buffer + reply->consume(count); +} + +QByteArray QNetworkAccessBackend::readUpstream() +{ + // ### this is expensive. Consider making QRingBuffer::peekAll keep the buffer it returns + return reply->writeBuffer.peek(upstreamBytesAvailable()); +} + +qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const +{ + return reply->nextDownstreamBlockSize(); +} + +qint64 QNetworkAccessBackend::downstreamBytesToConsume() const +{ + return reply->writeBuffer.size(); +} + +void QNetworkAccessBackend::writeDownstreamData(const QByteArray &data) +{ + reply->feed(data); +} + +void QNetworkAccessBackend::writeDownstreamData(QIODevice *data) +{ + reply->feed(data); +} + +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 +} + +QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h new file mode 100644 index 0000000..9012396 --- /dev/null +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + +// 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: + // Upstream is data that is being written into this connection, + // from the user. Upstream operates in a "pull" mechanism: the + // connection will be notified that there is more data available + // by a call to "upstreamReadyRead". The number of bytes + // available is given by upstreamBytesAvailable(). A call to + // readUpstream() always yields the entire upstream buffer. When + // the connection has processed a certain amount of bytes from + // that buffer, it should call upstreamBytesConsumed(). + // + // - 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; + virtual void closeDownstreamChannel() = 0; + virtual void closeUpstreamChannel() = 0; + virtual bool waitForDownstreamReadyRead(int msecs) = 0; + virtual bool waitForUpstreamBytesWritten(int msecs) = 0; + + // slot-like: + virtual void upstreamReadyRead(); + virtual void downstreamReadyWrite(); + virtual void copyFinished(QIODevice *); + virtual void ignoreSslErrors(); + + 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); + +protected: + // these functions control the upstream mechanism + // that is, data coming into the backend and out via the connection + qint64 upstreamBytesAvailable() const; + void upstreamBytesConsumed(qint64 count); + QByteArray readUpstream(); + + // these functions control the downstream mechanism + // that is, data that has come via the connection and is going out the backend + qint64 nextDownstreamBlockSize() const; + qint64 downstreamBytesToConsume() const; + void writeDownstreamData(const QByteArray &data); + 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); + +private: + friend class QNetworkAccessManager; + friend class QNetworkAccessManagerPrivate; + QNetworkAccessManagerPrivate *manager; + QNetworkReplyImplPrivate *reply; +}; + +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 0000000..9b2be3d --- /dev/null +++ b/src/network/access/qnetworkaccesscache.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..23d8828 --- /dev/null +++ b/src/network/access/qnetworkaccesscache_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + +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 0000000..fcb294f --- /dev/null +++ b/src/network/access/qnetworkaccesscachebackend.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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() +{ +} + +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); + + // 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!"); +} + +bool QNetworkAccessCacheBackend::waitForDownstreamReadyRead(int) +{ + Q_ASSERT_X(false, Q_FUNC_INFO , "This function show not have been called!"); + return false; +} + +bool QNetworkAccessCacheBackend::waitForUpstreamBytesWritten(int) +{ + Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!"); + return false; +} + +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 0000000..0d864f5 --- /dev/null +++ b/src/network/access/qnetworkaccesscachebackend_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(); + bool waitForDownstreamReadyRead(int msecs); + bool waitForUpstreamBytesWritten(int msecs); + + void upstreamReadyRead(); + void downstreamReadyWrite(); + +private: + bool sendCacheContents(); + QIODevice *device; + +}; + +QT_END_NAMESPACE + +#endif // QNETWORKACCESSCACHEBACKEND_P_H diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp new file mode 100644 index 0000000..f31247c --- /dev/null +++ b/src/network/access/qnetworkaccessdatabackend.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkaccessdatabackend_p.h" +#include "qnetworkrequest.h" +#include "qnetworkreply.h" +#include "qurlinfo.h" + +QT_BEGIN_NAMESPACE + +QNetworkAccessBackend * +QNetworkAccessDataBackendFactory::create(QNetworkAccessManager::Operation, + const QNetworkRequest &request) const +{ + if (request.url().scheme() == QLatin1String("data")) + return new QNetworkAccessDataBackend; + + return 0; +} + +QNetworkAccessDataBackend::QNetworkAccessDataBackend() +{ +} + +QNetworkAccessDataBackend::~QNetworkAccessDataBackend() +{ +} + +void QNetworkAccessDataBackend::open() +{ + QUrl uri = request().url(); + + if (operation() != QNetworkAccessManager::GetOperation && + operation() != QNetworkAccessManager::HeadOperation) { + // data: doesn't support anything but GET + QString msg = QObject::tr("Operation not supported on %1") + .arg(uri.toString()); + error(QNetworkReply::ContentOperationNotPermittedError, msg); + finished(); + return; + } + + if (uri.host().isEmpty()) { + setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/plain;charset=US-ASCII")); + + // the following would have been the correct thing, but + // reality often differs from the specification. People have + // data: URIs with ? and # + //QByteArray data = QByteArray::fromPercentEncoding(uri.encodedPath()); + QByteArray data = QByteArray::fromPercentEncoding(uri.toEncoded()); + + // remove the data: scheme + data.remove(0, 5); + + // parse it: + int pos = data.indexOf(','); + if (pos != -1) { + QByteArray payload = data.mid(pos + 1); + data.truncate(pos); + data = data.trimmed(); + + // find out if the payload is encoded in Base64 + if (data.endsWith(";base64")) { + payload = QByteArray::fromBase64(payload); + data.chop(7); + } + + if (data.toLower().startsWith("charset")) { + int i = 7; // strlen("charset") + while (data.at(i) == ' ') + ++i; + if (data.at(i) == '=') + data.prepend("text/plain;"); + } + + if (!data.isEmpty()) + setHeader(QNetworkRequest::ContentTypeHeader, data.trimmed()); + + setHeader(QNetworkRequest::ContentLengthHeader, payload.size()); + emit metaDataChanged(); + + writeDownstreamData(payload); + finished(); + return; + } + } + + // something wrong with this URI + QString msg = QObject::tr("Invalid URI: %1").arg(uri.toString()); + error(QNetworkReply::ProtocolFailure, msg); + finished(); +} + +void QNetworkAccessDataBackend::closeDownstreamChannel() +{ +} + +void QNetworkAccessDataBackend::closeUpstreamChannel() +{ +} + +bool QNetworkAccessDataBackend::waitForDownstreamReadyRead(int) +{ + return false; +} + +bool QNetworkAccessDataBackend::waitForUpstreamBytesWritten(int) +{ + return false; +} + +QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessdatabackend_p.h b/src/network/access/qnetworkaccessdatabackend_p.h new file mode 100644 index 0000000..54eca3d --- /dev/null +++ b/src/network/access/qnetworkaccessdatabackend_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKACCESSDATABACKEND_P_H +#define QNETWORKACCESSDATABACKEND_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" + +QT_BEGIN_NAMESPACE + +class QNetworkAccessDataBackend: public QNetworkAccessBackend +{ +public: + QNetworkAccessDataBackend(); + virtual ~QNetworkAccessDataBackend(); + + virtual void open(); + virtual void closeDownstreamChannel(); + virtual void closeUpstreamChannel(); + virtual bool waitForDownstreamReadyRead(int msecs); + virtual bool waitForUpstreamBytesWritten(int msecs); +}; + +class QNetworkAccessDataBackendFactory: public QNetworkAccessBackendFactory +{ +public: + virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op, + const QNetworkRequest &request) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp new file mode 100644 index 0000000..2e5f1b1 --- /dev/null +++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkaccessdebugpipebackend_p.h" +#include "QtCore/qdatastream.h" + +QT_BEGIN_NAMESPACE + +#ifdef QT_BUILD_INTERNAL + +enum { + ReadBufferSize = 16384, + WriteBufferSize = ReadBufferSize +}; + +struct QNetworkAccessDebugPipeBackend::DataPacket +{ + QList<QPair<QByteArray, QByteArray> > headers; + QByteArray data; +}; + +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() + : incomingPacketSize(0), bareProtocol(false) +{ +} + +QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend() +{ + 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); + connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead())); + connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64))); + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError())); + connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected())); + + bareProtocol = url().queryItemValue(QLatin1String("bare")) == QLatin1String("1"); + + if (!bareProtocol) { + // "Handshake": + // send outgoing metadata and the URL being requested + DataPacket packet; + //packet.metaData = request().metaData(); + packet.data = url().toEncoded(); + send(packet); + } +} + +void QNetworkAccessDebugPipeBackend::closeDownstreamChannel() +{ + if (operation() == QNetworkAccessManager::GetOperation) + socket.disconnectFromHost(); +} + +void QNetworkAccessDebugPipeBackend::closeUpstreamChannel() +{ + if (operation() == QNetworkAccessManager::PutOperation) + socket.disconnectFromHost(); + else if (operation() == QNetworkAccessManager::PostOperation) { + send(DataPacket()); + } +} + +bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms) +{ + readyReadEmitted = false; + if (socket.bytesAvailable()) { + socketReadyRead(); + if (readyReadEmitted) + return true; + } + socket.waitForReadyRead(ms); + return readyReadEmitted; +} + +bool QNetworkAccessDebugPipeBackend::waitForUpstreamBytesWritten(int ms) +{ + bytesWrittenEmitted = false; + upstreamReadyRead(); + if (bytesWrittenEmitted) + return true; + + socket.waitForBytesWritten(ms); + return bytesWrittenEmitted; +} + +void QNetworkAccessDebugPipeBackend::upstreamReadyRead() +{ + int maxWrite = WriteBufferSize - socket.bytesToWrite(); + if (maxWrite <= 0) + return; // can't write yet, wait for the socket to write + + if (bareProtocol) { + QByteArray data = readUpstream(); + if (data.isEmpty()) + return; + + socket.write(data); + upstreamBytesConsumed(data.size()); + bytesWrittenEmitted = true; + return; + } + + DataPacket packet; + packet.data = readUpstream(); + if (packet.data.isEmpty()) + return; // we'll be called again when there's data + if (packet.data.size() > maxWrite) + packet.data.truncate(maxWrite); + + if (!send(packet)) { + QString msg = QObject::tr("Write error writing to %1: %2") + .arg(url().toString(), socket.errorString()); + error(QNetworkReply::ProtocolFailure, msg); + + finished(); + return; + } + upstreamBytesConsumed(packet.data.size()); + bytesWrittenEmitted = true; +} + +void QNetworkAccessDebugPipeBackend::downstreamReadyWrite() +{ + socketReadyRead(); +} + +void QNetworkAccessDebugPipeBackend::socketReadyRead() +{ + if (bareProtocol) { + qint64 bytesToRead = socket.bytesAvailable(); + if (bytesToRead) { + QByteArray buffer; + buffer.resize(bytesToRead); + qint64 bytesRead = socket.read(buffer.data(), bytesToRead); + if (bytesRead < bytesToRead) + buffer.truncate(bytesRead); + writeDownstreamData(buffer); + readyReadEmitted = true; + } + return; + } + + while (canReceive() && + (socket.state() == QAbstractSocket::UnconnectedState || nextDownstreamBlockSize())) { + DataPacket packet; + if (receive(packet)) { + if (!packet.headers.isEmpty()) { + QList<QPair<QByteArray, QByteArray> >::ConstIterator + it = packet.headers.constBegin(), + end = packet.headers.constEnd(); + for ( ; it != end; ++it) + setRawHeader(it->first, it->second); + metaDataChanged(); + } + + if (!packet.data.isEmpty()) { + writeDownstreamData(packet.data); + readyReadEmitted = true; + } + + if (packet.headers.isEmpty() && packet.data.isEmpty()) { + // it's an eof + socket.close(); + readyReadEmitted = true; + } + } else { + // got an error + QString msg = QObject::tr("Read error reading from %1: %2") + .arg(url().toString(), socket.errorString()); + error(QNetworkReply::ProtocolFailure, msg); + + finished(); + return; + } + } +} + +void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64) +{ + upstreamReadyRead(); +} + +void QNetworkAccessDebugPipeBackend::socketError() +{ + 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, QObject::tr("Socket error on %1: %2") + .arg(url().toString(), socket.errorString())); + finished(); + disconnect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + +} + +void QNetworkAccessDebugPipeBackend::socketDisconnected() +{ + socketReadyRead(); + if (incomingPacketSize == 0 && socket.bytesToWrite() == 0) { + // normal close + finished(); + } else { + // abnormal close + QString msg = QObject::tr("Remote host closed the connection prematurely on %1") + .arg(url().toString()); + error(QNetworkReply::RemoteHostClosedError, msg); + + finished(); + } +} + +bool QNetworkAccessDebugPipeBackend::send(const DataPacket &packet) +{ + QByteArray ba; + { + QDataStream stream(&ba, QIODevice::WriteOnly); + stream.setVersion(QDataStream::Qt_4_4); + + stream << packet.headers << packet.data; + } + + qint32 outgoingPacketSize = ba.size(); + qint64 written = socket.write((const char*)&outgoingPacketSize, sizeof outgoingPacketSize); + written += socket.write(ba); + return quint64(written) == (outgoingPacketSize + sizeof outgoingPacketSize); +} + +bool QNetworkAccessDebugPipeBackend::receive(DataPacket &packet) +{ + if (!canReceive()) + return false; + + // canReceive() does the setting up for us + Q_ASSERT(socket.bytesAvailable() >= incomingPacketSize); + QByteArray incomingPacket = socket.read(incomingPacketSize); + QDataStream stream(&incomingPacket, QIODevice::ReadOnly); + stream.setVersion(QDataStream::Qt_4_4); + stream >> packet.headers >> packet.data; + + // reset for next packet: + incomingPacketSize = 0; + socket.setReadBufferSize(ReadBufferSize); + return true; +} + +bool QNetworkAccessDebugPipeBackend::canReceive() +{ + if (incomingPacketSize == 0) { + // read the packet size + if (quint64(socket.bytesAvailable()) >= sizeof incomingPacketSize) + socket.read((char*)&incomingPacketSize, sizeof incomingPacketSize); + else + return false; + } + + if (incomingPacketSize == 0) { + QString msg = QObject::tr("Protocol error: packet of size 0 received"); + error(QNetworkReply::ProtocolFailure, msg); + finished(); + + socket.blockSignals(true); + socket.abort(); + socket.blockSignals(false); + return false; + } + + return socket.bytesAvailable() >= incomingPacketSize; +} + +#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 0000000..73a35cf --- /dev/null +++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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: + struct DataPacket; + QNetworkAccessDebugPipeBackend(); + virtual ~QNetworkAccessDebugPipeBackend(); + + virtual void open(); + virtual void closeDownstreamChannel(); + virtual void closeUpstreamChannel(); + virtual bool waitForDownstreamReadyRead(int msecs); + virtual bool waitForUpstreamBytesWritten(int msecs); + + virtual void upstreamReadyRead(); + virtual void downstreamReadyWrite(); + +private slots: + void socketReadyRead(); + void socketBytesWritten(qint64 bytes); + void socketError(); + void socketDisconnected(); + +private: + QTcpSocket socket; + qint32 incomingPacketSize; + bool readyReadEmitted; + bool bytesWrittenEmitted; + bool bareProtocol; + + bool send(const DataPacket &packet); + bool canReceive(); + bool receive(DataPacket &packet); +}; + +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 0000000..8a5a665 --- /dev/null +++ b/src/network/access/qnetworkaccessfilebackend.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkaccessfilebackend_p.h" +#include "qfileinfo.h" +#include "qurlinfo.h" +#include "qdir.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() == QLatin1String("qrc") || !url.toLocalFile().isEmpty()) + return new QNetworkAccessFileBackend; + else if (!url.isEmpty() && url.authority().isEmpty()) { + // check if QFile could, in theory, open this URL + QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery)); + if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists())) + return new QNetworkAccessFileBackend; + } + + return 0; +} + +QNetworkAccessFileBackend::QNetworkAccessFileBackend() + : totalBytes(0) +{ +} + +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 = QLatin1String(":") + 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; + 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::closeDownstreamChannel() +{ + if (operation() == QNetworkAccessManager::GetOperation) { + file.close(); + //downstreamChannelClosed(); + } +} + +void QNetworkAccessFileBackend::closeUpstreamChannel() +{ + if (operation() == QNetworkAccessManager::PutOperation) { + file.close(); + finished(); + } +} + +bool QNetworkAccessFileBackend::waitForDownstreamReadyRead(int) +{ + Q_ASSERT(operation() == QNetworkAccessManager::GetOperation); + return readMoreFromFile(); +} + +bool QNetworkAccessFileBackend::waitForUpstreamBytesWritten(int) +{ + Q_ASSERT_X(false, "QNetworkAccessFileBackend::waitForUpstreamBytesWritten", + "This function should never have been called, since there is never anything " + "left to be written!"); + return false; +} + +void QNetworkAccessFileBackend::upstreamReadyRead() +{ + Q_ASSERT_X(operation() == QNetworkAccessManager::PutOperation, "QNetworkAccessFileBackend", + "We're being told to upload data but operation isn't PUT!"); + + // there's more data to be written to the file + while (upstreamBytesAvailable()) { + // write everything and let QFile handle it + int written = file.write(readUpstream()); + + if (written < 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; + } + + // successful write + file.flush(); + upstreamBytesConsumed(written); + } +} + +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; + writeDownstreamData(data); + } + 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 0000000..ce7d351 --- /dev/null +++ b/src/network/access/qnetworkaccessfilebackend_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 +{ +public: + QNetworkAccessFileBackend(); + virtual ~QNetworkAccessFileBackend(); + + virtual void open(); + virtual void closeDownstreamChannel(); + virtual void closeUpstreamChannel(); + virtual bool waitForDownstreamReadyRead(int msecs); + virtual bool waitForUpstreamBytesWritten(int msecs); + + virtual void upstreamReadyRead(); + virtual void downstreamReadyWrite(); + +private: + QFile file; + qint64 totalBytes; + + 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 0000000..ea39dec --- /dev/null +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -0,0 +1,441 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkaccessftpbackend_p.h" +#include "qnetworkaccessmanager_p.h" +#include "QtNetwork/qauthenticator.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() == QLatin1String("ftp")) + return new QNetworkAccessFtpBackend; + return 0; +} + +class QNetworkAccessFtpIODevice: public QIODevice +{ + //Q_OBJECT +public: + QNetworkAccessFtpBackend *backend; + bool eof; + + inline QNetworkAccessFtpIODevice(QNetworkAccessFtpBackend *parent) + : QIODevice(parent), backend(parent), eof(false) + { open(ReadOnly); } + + bool isSequential() const { return true; } + bool atEnd() const { return backend->upstreamBytesAvailable() == 0; } + + qint64 bytesAvailable() const { return backend->upstreamBytesAvailable(); } + qint64 bytesToWrite() const { return backend->downstreamBytesToConsume(); } +protected: + qint64 readData(char *data, qint64 maxlen) + { + const QByteArray toSend = backend->readUpstream(); + maxlen = qMin<qint64>(maxlen, toSend.size()); + if (!maxlen) + return eof ? -1 : 0; + + backend->upstreamBytesConsumed(maxlen); + memcpy(data, toSend.constData(), maxlen); + return maxlen; + } + + qint64 writeData(const char *, qint64) + { return -1; } + + friend class QNetworkAccessFtpBackend; +}; + +class QNetworkAccessFtpFtp: public QFtp, public QNetworkAccessCache::CacheableObject +{ + // Q_OBJECT +public: + QNetworkAccessFtpFtp() + { + 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* cache = QNetworkAccessManagerPrivate::getCache(this); + QByteArray cacheKey = makeCacheKey(url); + if (!cache->requestEntry(cacheKey, this, + SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) { + ftp = new QNetworkAccessFtpFtp; +#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()); + + cache->addEntry(cacheKey, ftp); + ftpConnectionReady(ftp); + } + + uploadDevice = new QNetworkAccessFtpIODevice(this); +} + +void QNetworkAccessFtpBackend::closeDownstreamChannel() +{ + state = Disconnecting; + if (operation() == QNetworkAccessManager::GetOperation) +#ifndef Q_OS_WINCE + abort(); +#else + exit(3); +#endif +} + +void QNetworkAccessFtpBackend::closeUpstreamChannel() +{ + if (operation() == QNetworkAccessManager::PutOperation) { + Q_ASSERT(uploadDevice); + uploadDevice->eof = true; + if (!upstreamBytesAvailable()) + emit uploadDevice->readyRead(); + } +} + +bool QNetworkAccessFtpBackend::waitForDownstreamReadyRead(int ms) +{ + if (!ftp) + return false; + + if (ftp->bytesAvailable()) { + ftpReadyRead(); + return true; + } + + if (ms == 0) + return false; + + qCritical("QNetworkAccess: FTP backend does not support waitForReadyRead()"); + return false; +} + +bool QNetworkAccessFtpBackend::waitForUpstreamBytesWritten(int ms) +{ + Q_UNUSED(ms); + qCritical("QNetworkAccess: FTP backend does not support waitForBytesWritten()"); + return false; +} + +void QNetworkAccessFtpBackend::upstreamReadyRead() +{ + // uh... how does QFtp operate? +} + +void QNetworkAccessFtpBackend::downstreamReadyWrite() +{ + if (state == Transferring && ftp && ftp->bytesAvailable()) + ftpReadyRead(); +} + +void QNetworkAccessFtpBackend::ftpConnectionReady(QNetworkAccessCache::CacheableObject *o) +{ + ftp = static_cast<QNetworkAccessFtpFtp *>(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::getCache(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::getCache(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) + 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() +{ + writeDownstreamData(ftp->readAll()); +} + +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 0000000..9ec2dd8 --- /dev/null +++ b/src/network/access/qnetworkaccessftpbackend_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 QNetworkAccessFtpFtp; + +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 closeUpstreamChannel(); + virtual bool waitForDownstreamReadyRead(int msecs); + virtual bool waitForUpstreamBytesWritten(int msecs); + + virtual void upstreamReadyRead(); + 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<QNetworkAccessFtpFtp> ftp; + QNetworkAccessFtpIODevice *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 0000000..4b41aa7 --- /dev/null +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -0,0 +1,1052 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "qnetworkrequest_p.h" +#include "qnetworkcookie_p.h" +#include "QtCore/qdatetime.h" +#include "QtNetwork/qsslconfiguration.h" + +#ifndef QT_NO_HTTP + +#include <string.h> // for strchr + +QT_BEGIN_NAMESPACE + +enum { + DefaultHttpPort = 80, + DefaultHttpsPort = 443 +}; + +class QNetworkProxy; + +static QByteArray makeCacheKey(QNetworkAccessHttpBackend *backend, QNetworkProxy *proxy) +{ + QByteArray result; + QUrl copy = backend->url(); + bool isEncrypted = copy.scheme().toLower() == QLatin1String("https"); + copy.setPort(copy.port(isEncrypted ? DefaultHttpsPort : DefaultHttpPort)); + result = copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | + QUrl::RemoveQuery | QUrl::RemoveFragment); + +#ifndef QT_NO_NETWORKPROXY + if (proxy->type() != QNetworkProxy::NoProxy) { + QUrl key; + + switch (proxy->type()) { + case QNetworkProxy::Socks5Proxy: + key.setScheme(QLatin1String("proxy-socks5")); + break; + + case QNetworkProxy::HttpProxy: + case QNetworkProxy::HttpCachingProxy: + key.setScheme(QLatin1String("proxy-http")); + break; + + default: + break; + } + + if (!key.scheme().isEmpty()) { + key.setUserName(proxy->user()); + key.setHost(proxy->hostName()); + key.setPort(proxy->port()); + key.setEncodedQuery(result); + result = key.toEncoded(); + } + } +#endif + + return "http-connection:" + result; +} + +static inline bool isSeparator(register char c) +{ + static const char separators[] = "()<>@,;:\\\"/[]?={}"; + 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 (equal != -1) { + // 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: + 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; +} + +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 407: + code = QNetworkReply::ProxyAuthenticationRequiredError; + break; + + default: + if (httpStatusCode > 500) { + // some kind of server error + code = QNetworkReply::ProtocolUnknownError; + } else if (httpStatusCode >= 400) { + // content error we did not handle above + code = QNetworkReply::UnknownContentError; + } else { + qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"", + httpStatusCode, qPrintable(url.toString())); + code = QNetworkReply::ProtocolFailure; + } + } + + return code; +} + +class QNetworkAccessHttpBackendCache: public QHttpNetworkConnection, + public QNetworkAccessCache::CacheableObject +{ + // Q_OBJECT +public: + QNetworkAccessHttpBackendCache(const QString &hostName, quint16 port, bool encrypt) + : QHttpNetworkConnection(hostName, port, encrypt) + { + setExpires(true); + setShareable(true); + } + + virtual void dispose() + { +#if 0 // sample code; do this right with the API + Q_ASSERT(!isWorking()); +#endif + delete this; + } +}; + +class QNetworkAccessHttpBackendIODevice: public QIODevice +{ + // Q_OBJECT +public: + bool eof; + QNetworkAccessHttpBackendIODevice(QNetworkAccessHttpBackend *parent) + : QIODevice(parent), eof(false) + { + setOpenMode(ReadOnly); + } + bool isSequential() const { return true; } + qint64 bytesAvailable() const + { return static_cast<QNetworkAccessHttpBackend *>(parent())->upstreamBytesAvailable(); } + +protected: + virtual qint64 readData(char *buffer, qint64 maxlen) + { + qint64 ret = static_cast<QNetworkAccessHttpBackend *>(parent())->deviceReadData(buffer, maxlen); + if (!ret && eof) + return -1; + return ret; + } + + virtual qint64 writeData(const char *, qint64) + { + return -1; // cannot write + } + + friend class QNetworkAccessHttpBackend; +}; + +QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() + : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0) +#ifndef QT_NO_OPENSSL + , pendingSslConfiguration(0), pendingIgnoreSslErrors(false) +#endif +{ +} + +QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend() +{ + if (http) + disconnectFromHttp(); +#ifndef QT_NO_OPENSSL + delete pendingSslConfiguration; +#endif +} + +void QNetworkAccessHttpBackend::disconnectFromHttp() +{ + if (http) { + disconnect(http, 0, this, 0); + QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getCache(this); + cache->releaseEntry(cacheKey); + } + + if (httpReply) + disconnect(httpReply, 0, this, 0); + + http = 0; + httpReply = 0; + cacheKey.clear(); +} + +void QNetworkAccessHttpBackend::finished() +{ + if (http) + disconnectFromHttp(); + // call parent + QNetworkAccessBackend::finished(); +} + +void QNetworkAccessHttpBackend::setupConnection() +{ +#ifndef QT_NO_NETWORKPROXY + connect(http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); +#endif + connect(http, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)), + SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*))); + connect(http, SIGNAL(error(QNetworkReply::NetworkError,QString)), + SLOT(httpError(QNetworkReply::NetworkError,QString))); +#ifndef QT_NO_OPENSSL + connect(http, SIGNAL(sslErrors(QList<QSslError>)), + SLOT(sslErrors(QList<QSslError>))); +#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 + */ +void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache) +{ + QNetworkRequest::CacheLoadControl CacheLoadControlAttribute = + (QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(); + if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) { + // forced reload from the network + // tell any caching proxy servers to reload too + httpRequest.setHeaderField("Cache-Control", "no-cache"); + httpRequest.setHeaderField("Pragma", "no-cache"); + return; + } + + QAbstractNetworkCache *nc = networkCache(); + if (!nc) + return; // no local cache + + QNetworkCacheMetaData metaData = nc->metaData(url()); + if (!metaData.isValid()) + return; // not in cache + + if (!metaData.saveToDisk()) + return; + + 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)); + + it = cacheHeaders.findRawHeader("Cache-Control"); + if (it != cacheHeaders.rawHeaders.constEnd()) { + QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second); + if (cacheControl.contains("must-revalidate")) + return; + } + + /* + * 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 + */ + QDateTime currentDateTime = QDateTime::currentDateTime(); + int age_value = 0; + it = cacheHeaders.findRawHeader("age"); + if (it != cacheHeaders.rawHeaders.constEnd()) + age_value = QNetworkHeadersPrivate::fromHttpDate(it->second).toTime_t(); + + int date_value = 0; + it = cacheHeaders.findRawHeader("date"); + if (it != cacheHeaders.rawHeaders.constEnd()) + date_value = QNetworkHeadersPrivate::fromHttpDate(it->second).toTime_t(); + + int now = currentDateTime.toUTC().toTime_t(); + int request_time = now; + int response_time = now; + + 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 + QDateTime expirationDate = metaData.expirationDate(); + 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"); + } + } + } + + int freshness_lifetime = currentDateTime.secsTo(expirationDate); + bool response_is_fresh = (freshness_lifetime > current_age); + + if (!response_is_fresh && CacheLoadControlAttribute == QNetworkRequest::PreferNetwork) + return; + + loadedFromCache = true; +#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) + qDebug() << "response_is_fresh" << CacheLoadControlAttribute; +#endif + if (!sendCacheContents(metaData)) + loadedFromCache = false; +} + +void QNetworkAccessHttpBackend::postRequest() +{ + bool loadedFromCache = false; + QHttpNetworkRequest httpRequest; + switch (operation()) { + case QNetworkAccessManager::GetOperation: + httpRequest.setOperation(QHttpNetworkRequest::Get); + validateCache(httpRequest, loadedFromCache); + break; + + case QNetworkAccessManager::HeadOperation: + httpRequest.setOperation(QHttpNetworkRequest::Head); + validateCache(httpRequest, loadedFromCache); + break; + + case QNetworkAccessManager::PostOperation: + invalidateCache(); + httpRequest.setOperation(QHttpNetworkRequest::Post); + uploadDevice = new QNetworkAccessHttpBackendIODevice(this); + break; + + case QNetworkAccessManager::PutOperation: + invalidateCache(); + httpRequest.setOperation(QHttpNetworkRequest::Put); + uploadDevice = new QNetworkAccessHttpBackendIODevice(this); + break; + + default: + break; // can't happen + } + + httpRequest.setData(uploadDevice); + httpRequest.setUrl(url()); + + QList<QByteArray> headers = request().rawHeaderList(); + foreach (const QByteArray &header, headers) + httpRequest.setHeaderField(header, request().rawHeader(header)); + + if (loadedFromCache) + return; // no need to send the request! :) + + httpReply = http->sendRequest(httpRequest); + httpReply->setParent(this); +#ifndef QT_NO_OPENSSL + if (pendingSslConfiguration) + httpReply->setSslConfiguration(*pendingSslConfiguration); + if (pendingIgnoreSslErrors) + httpReply->ignoreSslErrors(); +#endif + + connect(httpReply, SIGNAL(readyRead()), SLOT(replyReadyRead())); + connect(httpReply, SIGNAL(finished()), SLOT(replyFinished())); + connect(httpReply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)), + SLOT(httpError(QNetworkReply::NetworkError,QString))); + connect(httpReply, SIGNAL(headerChanged()), SLOT(replyHeaderChanged())); +} + +void QNetworkAccessHttpBackend::invalidateCache() +{ + QAbstractNetworkCache *nc = networkCache(); + if (nc) + nc->remove(url()); +} + +void QNetworkAccessHttpBackend::open() +{ + QUrl url = request().url(); + bool encrypt = url.scheme().toLower() == QLatin1String("https"); + setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, encrypt); + + // set the port number in the reply if it wasn't set + url.setPort(url.port(encrypt ? DefaultHttpsPort : DefaultHttpPort)); + + QNetworkProxy *theProxy = 0; +#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 (!encrypt + && (p.capabilities() & QNetworkProxy::CachingCapability) + && (p.type() == QNetworkProxy::HttpProxy || + p.type() == QNetworkProxy::HttpCachingProxy)) { + cacheProxy = p; + transparentProxy = QNetworkProxy::NoProxy; + theProxy = &cacheProxy; + break; + } + if (p.isTransparentProxy()) { + transparentProxy = p; + cacheProxy = QNetworkProxy::NoProxy; + theProxy = &transparentProxy; + break; + } + } + + // check if at least one of the proxies + if (transparentProxy.type() == QNetworkProxy::DefaultProxy && + cacheProxy.type() == QNetworkProxy::DefaultProxy) { + // unsuitable proxies + error(QNetworkReply::ProxyNotFoundError, + tr("No suitable proxy found")); + finished(); + return; + } +#endif + + // check if we have an open connection to this host + cacheKey = makeCacheKey(this, theProxy); + QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getCache(this); + if ((http = static_cast<QNetworkAccessHttpBackendCache *>(cache->requestEntryNow(cacheKey))) == 0) { + // no entry in cache; create an object + http = new QNetworkAccessHttpBackendCache(url.host(), url.port(), encrypt); + +#ifndef QT_NO_NETWORKPROXY + http->setTransparentProxy(transparentProxy); + http->setCacheProxy(cacheProxy); +#endif + + cache->addEntry(cacheKey, http); + } + + setupConnection(); + postRequest(); +} + +void QNetworkAccessHttpBackend::closeDownstreamChannel() +{ + // this indicates that the user closed the stream while the reply isn't finished yet +} + +void QNetworkAccessHttpBackend::closeUpstreamChannel() +{ + // this indicates that the user finished uploading the data for POST + Q_ASSERT(uploadDevice); + uploadDevice->eof = true; + emit uploadDevice->readChannelFinished(); +} + +bool QNetworkAccessHttpBackend::waitForDownstreamReadyRead(int msecs) +{ + Q_ASSERT(http); + + if (httpReply->bytesAvailable()) { + readFromHttp(); + return true; + } + + if (msecs == 0) { + // no bytes available in the socket and no waiting + return false; + } + + // ### FIXME + qCritical("QNetworkAccess: HTTP backend does not support waitForReadyRead()"); + return false; +} + +bool QNetworkAccessHttpBackend::waitForUpstreamBytesWritten(int msecs) +{ + + // ### FIXME: not implemented in QHttpNetworkAccess + Q_UNUSED(msecs); + qCritical("QNetworkAccess: HTTP backend does not support waitForBytesWritten()"); + return false; +} + +void QNetworkAccessHttpBackend::upstreamReadyRead() +{ + // There is more data available from the user to be uploaded + // QHttpNetworkAccess implements the upload rate control: + // we simply tell QHttpNetworkAccess that there is more data available + // it'll pull from us when it can (through uploadDevice) + + Q_ASSERT(uploadDevice); + emit uploadDevice->readyRead(); +} + +qint64 QNetworkAccessHttpBackend::deviceReadData(char *buffer, qint64 maxlen) +{ + QByteArray toBeUploaded = readUpstream(); + if (toBeUploaded.isEmpty()) + return 0; // nothing to be uploaded + + maxlen = qMin<qint64>(maxlen, toBeUploaded.length()); + + memcpy(buffer, toBeUploaded.constData(), maxlen); + upstreamBytesConsumed(maxlen); + return maxlen; +} + +void QNetworkAccessHttpBackend::downstreamReadyWrite() +{ + readFromHttp(); + if (httpReply && httpReply->bytesAvailable() == 0 && httpReply->isFinished()) + replyFinished(); +} + +void QNetworkAccessHttpBackend::replyReadyRead() +{ + readFromHttp(); +} + +void QNetworkAccessHttpBackend::readFromHttp() +{ + if (!httpReply) + return; + + // We implement the download rate control + // Don't read from QHttpNetworkAccess more than QNetworkAccessBackend wants + // One of the two functions above will be called when we can read again + + qint64 bytesToRead = qBound<qint64>(0, httpReply->bytesAvailable(), nextDownstreamBlockSize()); + if (!bytesToRead) + return; + + QByteArray data = httpReply->read(bytesToRead); + writeDownstreamData(data); +} + +void QNetworkAccessHttpBackend::replyFinished() +{ + if (httpReply->bytesAvailable()) + // we haven't read everything yet. Wait some more. + return; + + int statusCode = httpReply->statusCode(); + if (statusCode >= 400) { + // it's an error reply + QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply", + "Error downloading %1 - server replied: %2")); + msg = msg.arg(url().toString(), httpReply->reasonPhrase()); + error(statusCodeFromHttp(httpReply->statusCode(), httpReply->url()), msg); + } + +#ifndef QT_NO_OPENSSL + // store the SSL configuration now + // once we call finished(), we won't have access to httpReply anymore + QSslConfiguration sslConfig = httpReply->sslConfiguration(); + if (pendingSslConfiguration) + *pendingSslConfiguration = sslConfig; + else if (!sslConfig.isNull()) + pendingSslConfiguration = new QSslConfiguration(sslConfig); +#endif + + 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::replyHeaderChanged() +{ + // reconstruct the HTTP header + QList<QPair<QByteArray, QByteArray> > headerMap = httpReply->header(); + 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()) + value += ", "; + value += it->second; + setRawHeader(it->first, value); + } + + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, httpReply->statusCode()); + setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase()); + + // is it a redirection? + const int statusCode = httpReply->statusCode(); + 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::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 +#if 0 + static const QNetworkReply::NetworkError conversionTable[] = { + QNetworkReply::ConnectionRefusedError, + QNetworkReply::RemoteHostClosedError, + QNetworkReply::HostNotFoundError, + QNetworkReply::UnknownNetworkError, // SocketAccessError + QNetworkReply::UnknownNetworkError, // SocketResourceError + QNetworkReply::TimeoutError, // SocketTimeoutError + QNetworkReply::UnknownNetworkError, // DatagramTooLargeError + QNetworkReply::UnknownNetworkError, // NetworkError + QNetworkReply::UnknownNetworkError, // AddressInUseError + QNetworkReply::UnknownNetworkError, // SocketAddressNotAvailableError + QNetworkReply::UnknownNetworkError, // UnsupportedSocketOperationError + QNetworkReply::UnknownNetworkError, // UnfinishedSocketOperationError + QNetworkReply::ProxyAuthenticationRequiredError + }; + QNetworkReply::NetworkError code; + if (int(errorCode) >= 0 && + uint(errorCode) < (sizeof conversionTable / sizeof conversionTable[0])) + code = conversionTable[errorCode]; + else + code = QNetworkReply::UnknownNetworkError; +#endif + error(errorCode, errorString); + finished(); +} + +/* + 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 + + checkForRedirect(status); + + 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); + + writeDownstreamData(contents); +#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) + qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes"; +#endif + if (httpReply) + disconnect(httpReply, SIGNAL(finished()), this, SLOT(replyFinished())); + return true; +} + +void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev) +{ + delete dev; + finished(); +} + +#ifndef QT_NO_OPENSSL +void QNetworkAccessHttpBackend::ignoreSslErrors() +{ + if (httpReply) + httpReply->ignoreSslErrors(); + else + pendingIgnoreSslErrors = true; +} + +void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const +{ + if (httpReply) + config = httpReply->sslConfiguration(); + else if (pendingSslConfiguration) + config = *pendingSslConfiguration; +} + +void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig) +{ + if (httpReply) + httpReply->setSslConfiguration(newconfig); + else if (pendingSslConfiguration) + *pendingSslConfiguration = newconfig; + else + pendingSslConfiguration = new QSslConfiguration(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) { + 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; + + // 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; + } + +#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) + QByteArray n = rawHeader(header); + QByteArray o; + it = cacheHeaders.findRawHeader(header); + 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(header, 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 = true; // Everything defaults to being cacheable on disk + + // 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; + + metaData.setSaveToDisk(canDiskCache); + int statusCode = httpReply->statusCode(); + QNetworkCacheMetaData::AttributesMap attributes; + if (statusCode != 304) { + // update the status code + attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode); + attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, httpReply->reasonPhrase()); + } else { + // this is a redirection, keep the attributes intact + attributes = oldMetaData.attributes(); + } + metaData.setAttributes(attributes); + return metaData; +} + +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 0000000..02915e7 --- /dev/null +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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" + +#ifndef QT_NO_HTTP + +QT_BEGIN_NAMESPACE + +class QNetworkAccessHttpBackendCache; + +class QNetworkAccessHttpBackendIODevice; + +class QNetworkAccessHttpBackend: public QNetworkAccessBackend +{ + Q_OBJECT +public: + QNetworkAccessHttpBackend(); + virtual ~QNetworkAccessHttpBackend(); + + virtual void open(); + virtual void closeDownstreamChannel(); + virtual void closeUpstreamChannel(); + virtual bool waitForDownstreamReadyRead(int msecs); + virtual bool waitForUpstreamBytesWritten(int msecs); + + virtual void upstreamReadyRead(); + virtual void downstreamReadyWrite(); + virtual void copyFinished(QIODevice *); +#ifndef QT_NO_OPENSSL + virtual void ignoreSslErrors(); + + virtual void fetchSslConfiguration(QSslConfiguration &configuration) const; + virtual void setSslConfiguration(const QSslConfiguration &configuration); +#endif + QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const; + + qint64 deviceReadData(char *buffer, qint64 maxlen); + +private slots: + void replyReadyRead(); + void replyFinished(); + void replyHeaderChanged(); + void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth); + void httpError(QNetworkReply::NetworkError error, const QString &errorString); + bool sendCacheContents(const QNetworkCacheMetaData &metaData); + +private: + QHttpNetworkReply *httpReply; + QPointer<QNetworkAccessHttpBackendCache> http; + QByteArray cacheKey; + QNetworkAccessHttpBackendIODevice *uploadDevice; +#ifndef QT_NO_OPENSSL + QSslConfiguration *pendingSslConfiguration; + bool pendingIgnoreSslErrors; +#endif + + void disconnectFromHttp(); + void finished(); // override + void setupConnection(); + void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache); + void invalidateCache(); + void postRequest(); + void readFromHttp(); + void checkForRedirect(const int statusCode); + + friend class QNetworkAccessHttpBackendIODevice; +}; + +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 0000000..11e1e46 --- /dev/null +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -0,0 +1,961 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "qnetworkaccesshttpbackend_p.h" +#include "qnetworkaccessftpbackend_p.h" +#include "qnetworkaccessfilebackend_p.h" +#include "qnetworkaccessdatabackend_p.h" +#include "qnetworkaccessdebugpipebackend_p.h" + +#include "QtCore/qbuffer.h" +#include "QtCore/qurl.h" +#include "QtCore/qvector.h" +#include "QtNetwork/qauthenticator.h" +#include "QtNetwork/qsslconfiguration.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_HTTP +Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend) +#endif // QT_NO_HTTP +Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) +Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend) +#ifndef QT_NO_FTP +Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend) +#endif // QT_NO_FTP + +#ifdef QT_BUILD_INTERNAL +Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend) +#endif + +static void ensureInitialized() +{ +#ifndef QT_NO_HTTP + (void) httpBackend(); +#endif // QT_NO_HTTP + (void) dataBackend(); +#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 + post network requests and receive replies + \since 4.4 + + \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. + + 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. + the reply to is where most of the signals as well + as the downloaded data are posted. + + A simple download off the network could be accomplished with: + \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 0 + + 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 The slot is responsible for deleting the object at that point. + + A more involved example, assuming the manager is already existent, + can be: + \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 1 + + \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()) + + \omitvalue UnknownOperation + + \sa QNetworkReply::operation() +*/ + +/*! + \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. + + \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() +*/ + +class QNetworkAuthenticationCredential +{ +public: + QString domain; + QString user; + QString password; +}; +Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE); +inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2) +{ return t1.domain < t2; } + +class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>, + public QNetworkAccessCache::CacheableObject +{ +public: + QNetworkAuthenticationCache() + { + setExpires(false); + setShareable(true); + reserve(1); + } + + QNetworkAuthenticationCredential *findClosestMatch(const QString &domain) + { + iterator it = qLowerBound(begin(), end(), domain); + if (it == end() && !isEmpty()) + --it; + if (it == end() || !domain.startsWith(it->domain)) + return 0; + return &*it; + } + + void insert(const QString &domain, const QString &user, const QString &password) + { + QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain); + if (closestMatch && closestMatch->domain == domain) { + // we're overriding the current credentials + closestMatch->user = user; + closestMatch->password = password; + } else { + QNetworkAuthenticationCredential newCredential; + newCredential.domain = domain; + newCredential.user = user; + newCredential.password = password; + + if (closestMatch) + QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential); + else + QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential); + } + } + + virtual void dispose() { delete this; } +}; + +#ifndef QT_NO_NETWORKPROXY +static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm) +{ + QUrl key; + + switch (proxy.type()) { + case QNetworkProxy::Socks5Proxy: + key.setScheme(QLatin1String("proxy-socks5")); + break; + + case QNetworkProxy::HttpProxy: + case QNetworkProxy::HttpCachingProxy: + key.setScheme(QLatin1String("proxy-http")); + break; + + case QNetworkProxy::FtpCachingProxy: + key.setScheme(QLatin1String("proxy-ftp")); + + case QNetworkProxy::DefaultProxy: + case QNetworkProxy::NoProxy: + // shouldn't happen + return QByteArray(); + + // no default: + // let there be errors if a new proxy type is added in the future + } + + if (key.scheme().isEmpty()) + // proxy type not handled + return QByteArray(); + + key.setUserName(proxy.user()); + key.setHost(proxy.hostName()); + key.setPort(proxy.port()); + key.setFragment(realm); + return "auth:" + key.toEncoded(); +} +#endif + +static inline QByteArray authenticationKey(const QUrl &url, const QString &realm) +{ + QUrl copy = url; + copy.setFragment(realm); + return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery); +} + +/*! + Constructs a QNetworkAccessManager object that is the center of + the Network Access API and sets \a parent as the parent object. +*/ +QNetworkAccessManager::QNetworkAccessManager(QObject *parent) + : QObject(*new QNetworkAccessManagerPrivate, parent) +{ + ensureInitialized(); +} + +/*! + 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 +} + +#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; + 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. + + QNetworkAccessManager will set the parent of the \a cookieJar + passed to itself, 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; + d->cookieJar->setParent(this); + } +} + +/*! + This function is used to post a request to obtain the network + headers for \a request. It takes its name after the HTTP request + associated (HEAD). It returns a new QNetworkReply object which + will contain such headers. +*/ +QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request) +{ + return d_func()->postProcess(createRequest(QNetworkAccessManager::HeadOperation, request)); +} + +/*! + This function is used to post a request to obtain the contents of + the target \a request. It will cause the contents to be + downloaded, along with the headers associated with it. It returns + a new QNetworkReply object opened for reading which emits its + QIODevice::readyRead() signal whenever new data arrives. + + \sa post(), put() +*/ +QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) +{ + return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request)); +} + +/*! + This function is used to send an HTTP POST request to the + destination specified by \a request. The contents of the \a data + device will be uploaded to the server. + + \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. + + The returned QNetworkReply object will be open for reading and + will contain the reply sent by the server to the POST request. + + Note: sending a POST request on protocols other than HTTP and + HTTPS is undefined and will probably fail. + + \sa get(), put() +*/ +QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data) +{ + return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data)); +} + +/*! + \overload + This function 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; +} + +/*! + This function is used to upload the contents of \a data to the + destination \a request. + + \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. + + The returned QNetworkReply object will be open for reply, but + whether anything will be available for reading 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. + + 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() +*/ +QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data) +{ + return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data)); +} + +/*! + \overload + This function 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; +} + +/*! + 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); + 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 (d->cookieJar) { + QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url()); + if (!cookies.isEmpty()) + request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies)); + } + + // first step: create the reply + QUrl url = request.url(); + QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); + QNetworkReplyImplPrivate *priv = reply->d_func(); + priv->manager = this; + + // second step: fetch cached credentials + QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url); + if (cred) { + url.setUserName(cred->user); + url.setPassword(cred->password); + priv->urlForLastAuthentication = url; + } + + // third step: setup the reply + priv->setup(op, request, outgoingData); + if (request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt() != + QNetworkRequest::AlwaysNetwork) + priv->setNetworkCache(d->networkCache); +#ifndef QT_NO_NETWORKPROXY + QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url())); + priv->proxyList = proxyList; +#endif + + // fourth step: find a backend + priv->backend = d->findBackend(op, request); + if (priv->backend) { + priv->backend->setParent(reply); + priv->backend->reply = priv; + } + +#ifndef QT_NO_OPENSSL + reply->setSslConfiguration(request.sslConfiguration()); +#endif + return reply; +} + +void QNetworkAccessManagerPrivate::_q_replyFinished() +{ + Q_Q(QNetworkAccessManager); + QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender()); + if (reply) + emit q->finished(reply); +} + +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->cookieJarCreated = true; + that->cookieJar = new QNetworkCookieJar(that->q_func()); + } +} + +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 + if (url != backend->reply->urlForLastAuthentication) { + QNetworkAuthenticationCredential *cred = fetchCachedCredentials(url, authenticator); + if (cred) { + authenticator->setUser(cred->user); + authenticator->setPassword(cred->password); + backend->reply->urlForLastAuthentication = url; + return; + } + } + + backend->reply->urlForLastAuthentication = url; + emit q->authenticationRequired(backend->reply->q_func(), authenticator); + addCredentials(url, authenticator); +} + +#ifndef QT_NO_NETWORKPROXY +void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend, + const QNetworkProxy &proxy, + QAuthenticator *authenticator) +{ + Q_Q(QNetworkAccessManager); + + if (proxy != backend->reply->lastProxyAuthentication) { + QNetworkAuthenticationCredential *cred = fetchCachedCredentials(proxy); + if (cred) { + authenticator->setUser(cred->user); + authenticator->setPassword(cred->password); + return; + } + } + + backend->reply->lastProxyAuthentication = proxy; + emit q->proxyAuthenticationRequired(proxy, authenticator); + addCredentials(proxy, authenticator); +} + +void QNetworkAccessManagerPrivate::addCredentials(const QNetworkProxy &p, + const QAuthenticator *authenticator) +{ + Q_ASSERT(authenticator); + Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy); + Q_ASSERT(p.type() != QNetworkProxy::NoProxy); + + QString realm = authenticator->realm(); + QNetworkProxy proxy = p; + proxy.setUser(authenticator->user()); + // Set two credentials: one with the username and one without + do { + // Set two credentials actually: one with and one without the realm + do { + QByteArray cacheKey = proxyAuthenticationKey(proxy, realm); + if (cacheKey.isEmpty()) + return; // should not happen + + QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache; + auth->insert(QString(), authenticator->user(), authenticator->password()); + cache.addEntry(cacheKey, auth); // replace the existing one, if there's any + + if (realm.isEmpty()) { + break; + } else { + realm.clear(); + } + } while (true); + + if (proxy.user().isEmpty()) + break; + else + proxy.setUser(QString()); + } while (true); +} + +QNetworkAuthenticationCredential * +QNetworkAccessManagerPrivate::fetchCachedCredentials(const QNetworkProxy &p, + const QAuthenticator *authenticator) +{ + QNetworkProxy proxy = p; + if (proxy.type() == QNetworkProxy::DefaultProxy) { + proxy = QNetworkProxy::applicationProxy(); + } + if (!proxy.password().isEmpty()) + return 0; // no need to set credentials if it already has them + + QString realm; + if (authenticator) + realm = authenticator->realm(); + + QByteArray cacheKey = proxyAuthenticationKey(proxy, realm); + if (cacheKey.isEmpty()) + return 0; + if (!cache.hasEntry(cacheKey)) + return 0; + + QNetworkAuthenticationCache *auth = + static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey)); + QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString()); + cache.releaseEntry(cacheKey); + + // proxy cache credentials always have exactly one item + Q_ASSERT_X(cred, "QNetworkAccessManager", + "Internal inconsistency: found a cache key for a proxy, but it's empty"); + return cred; +} + +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::addCredentials(const QUrl &url, + const QAuthenticator *authenticator) +{ + Q_ASSERT(authenticator); + QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain + QString realm = authenticator->realm(); + + // Set two credentials actually: one with and one without the username in the URL + QUrl copy = url; + copy.setUserName(authenticator->user()); + do { + QByteArray cacheKey = authenticationKey(copy, realm); + if (cache.hasEntry(cacheKey)) { + QNetworkAuthenticationCache *auth = + static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey)); + auth->insert(domain, authenticator->user(), authenticator->password()); + cache.releaseEntry(cacheKey); + } else { + QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache; + auth->insert(domain, authenticator->user(), authenticator->password()); + cache.addEntry(cacheKey, auth); + } + + if (copy.userName().isEmpty()) { + break; + } else { + copy.setUserName(QString()); + } + } while (true); +} + +/*! + Fetch the credential data from the credential cache. + + If auth is 0 (as it is when called from createRequest()), this will try to + look up with an empty realm. That fails in most cases for HTTP (because the + realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection + never sends the credentials on the first attempt: it needs to find out what + authentication methods the server supports. + + For FTP, realm is always empty. +*/ +QNetworkAuthenticationCredential * +QNetworkAccessManagerPrivate::fetchCachedCredentials(const QUrl &url, + const QAuthenticator *authentication) +{ + if (!url.password().isEmpty()) + return 0; // no need to set credentials if it already has them + + QString realm; + if (authentication) + realm = authentication->realm(); + + QByteArray cacheKey = authenticationKey(url, realm); + if (!cache.hasEntry(cacheKey)) + return 0; + + QNetworkAuthenticationCache *auth = + static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey)); + QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path()); + cache.releaseEntry(cacheKey); + return cred; +} + +void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager) +{ + manager->d_func()->cache.clear(); +} + +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 0000000..4fe218e --- /dev/null +++ b/src/network/access/qnetworkaccessmanager.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + +class QNetworkReplyImplPrivate; +class QNetworkAccessManagerPrivate; +class Q_NETWORK_EXPORT QNetworkAccessManager: public QObject +{ + Q_OBJECT +public: + enum Operation { + HeadOperation = 1, + GetOperation, + PutOperation, + PostOperation, + + UnknownOperation = 0 + }; + + 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 *put(const QNetworkRequest &request, QIODevice *data); + QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data); + +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 + +protected: + virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, + QIODevice *outgoingData = 0); + +private: + friend class QNetworkReplyImplPrivate; + Q_DECLARE_PRIVATE(QNetworkAccessManager) + Q_PRIVATE_SLOT(d_func(), void _q_replyFinished()) + Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>)) +}; + +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 0000000..665d143 --- /dev/null +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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" + +QT_BEGIN_NAMESPACE + +class QAuthenticator; +class QAbstractNetworkCache; +class QNetworkAuthenticationCredential; +class QNetworkCookieJar; + +class QNetworkAccessManagerPrivate: public QObjectPrivate +{ +public: + QNetworkAccessManagerPrivate() + : networkCache(0), cookieJar(0), +#ifndef QT_NO_NETWORKPROXY + proxyFactory(0), +#endif + cookieJarCreated(false) + { } + + void _q_replyFinished(); + void _q_replySslErrors(const QList<QSslError> &errors); + QNetworkReply *postProcess(QNetworkReply *reply); + void createCookieJar() const; + + void authenticationRequired(QNetworkAccessBackend *backend, QAuthenticator *authenticator); + void addCredentials(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 addCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth); + QNetworkAuthenticationCredential *fetchCachedCredentials(const QNetworkProxy &proxy, + const QAuthenticator *auth = 0); + QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query); +#endif + + QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request); + + QAbstractNetworkCache *networkCache; + QNetworkCookieJar *cookieJar; + + QNetworkAccessCache cache; +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy proxy; + QNetworkProxyFactory *proxyFactory; +#endif + + bool cookieJarCreated; + + static inline QNetworkAccessCache *getCache(QNetworkAccessBackend *backend) + { return &backend->manager->cache; } + 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 0000000..1235960 --- /dev/null +++ b/src/network/access/qnetworkcookie.cpp @@ -0,0 +1,932 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/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 == other.d->expirationDate && + 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) +{ + // 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 + ++i; + while (i < length) { + register char c = text.at(i); + if (c == '"') { + // end of quoted text + break; + } else if (c == '\\') { + ++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('"')) { + 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="; + result += QUrl::toAce(d->domain); + } + if (!d->path.isEmpty()) { + result += "; path="; + result += QUrl::toPercentEncoding(d->path, "/"); + } + } + return result; +} + +/*! + 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) +{ + // 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); + if (field.first.isEmpty() || field.second.isNull()) + // parsing error + return QList<QNetworkCookie>(); + 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); + field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive + + if (field.first == "expires") { + static const char dateFormats[] = + "d-MMM-yyyy hh:mm:ss\0" + "d MMM yyyy hh:mm:ss\0" + "d-MMM-yy hh:mm:ss\0" + "\0"; + + // expires is a special case because it contains a naked comma + // and naked spaces. The format is: + // expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT + // but we also accept standard HTTP dates + + // make sure we're at the comma + if (position >= length || cookieString.at(position) != ',') + // invalid cookie string + return QList<QNetworkCookie>(); + + ++position; + int end; + for (end = position; end < length; ++end) + if (cookieString.at(end) == ',' || cookieString.at(end) == ';') + break; + + QByteArray datestring = cookieString.mid(position, end - position).trimmed(); + position = end; + if (datestring.endsWith(" GMT") || datestring.endsWith(" UTC")) + datestring.chop(4); + else if (datestring.endsWith(" +0000")) + datestring.chop(6); + + size_t i = 0; + int j = 0; + QLocale cLocale = QLocale::c(); + QDateTime dt; + do { + QLatin1String df(dateFormats + i); + i += strlen(dateFormats + i) + 1; + +#ifndef QT_NO_DATESTRING + dt = cLocale.toDateTime(QString::fromLatin1(datestring), df); + + // some cookies are set with a two-digit year + // (although this is not allowed); this is interpreted as a year + // in the 20th century by QDateTime. + // Work around this case here (assuming 00-69 is 21st century, + // 70-99 is 20th century) + QDate date = dt.date(); + if (j == 2 && date.year() >= 1900 && date.year() < 1970) + dt = dt.addYears(100); + if (date.year() >= 0 && date.year() < 100) + dt = dt.addYears(1900); +#endif + j++; + } while (!dt.isValid() && i <= sizeof dateFormats - 1); + if (!dt.isValid()) + // invalid cookie string + return QList<QNetworkCookie>(); + + dt.setTimeSpec(Qt::UTC); + 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))); + cookie.setDomain(maybeLeadingDot + normalizedDomain); + } else if (field.first == "max-age") { + bool ok = false; + int secs = field.second.toInt(&ok); + if (!ok) + // invalid cookie string + return QList<QNetworkCookie>(); + 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 + cookie = QNetworkCookie(); + endOfCookie = true; + continue; + } + } else { + // got an unknown field in the cookie + // what do we do? + } + + position = nextNonWhitespace(cookieString, position); + } + + 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 + + + +class QNetworkCookieJarPrivate: public QObjectPrivate +{ +public: + QList<QNetworkCookie> allCookies; + + Q_DECLARE_PUBLIC(QNetworkCookieJar) +}; + +/*! + \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 cookes are set for 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 + // (RFC 2965: "The request-URI MUST path-match the Path attribute of the cookie.") + if (cookie.path().isEmpty()) + cookie.setPath(defaultPath); + else if (!isParentPath(pathAndFileName, cookie.path())) + continue; // not accepted + + if (cookie.domain().isEmpty()) { + cookie.setDomain(defaultDomain); + } else { + QString domain = cookie.domain(); + if (!(isParentDomain(domain, defaultDomain) + || isParentDomain(defaultDomain, domain))) { + 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 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; + + // 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; + + // 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; +} + +QT_END_NAMESPACE diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h new file mode 100644 index 0000000..e8dfab0 --- /dev/null +++ b/src/network/access/qnetworkcookie.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + +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) +}; + +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QNetworkCookie &); +#endif + +QT_END_NAMESPACE + +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 0000000..83ef14a --- /dev/null +++ b/src/network/access/qnetworkcookie_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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) { } + + 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/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp new file mode 100644 index 0000000..fa0fccb --- /dev/null +++ b/src/network/access/qnetworkdiskcache.cpp @@ -0,0 +1,666 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QNETWORKDISKCACHE_DEBUG + +#include "qnetworkdiskcache.h" +#include "qnetworkdiskcache_p.h" + +#include <qfile.h> +#include <qdir.h> +#include <qdatetime.h> +#include <qdiriterator.h> +#include <qcryptographichash.h> +#include <qurl.h> + +#include <qdebug.h> + +#define CACHE_PREFIX QLatin1String("cache_") +#define CACHE_POSTFIX QLatin1String(".cache") +#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3) + +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. +*/ + +/*! + 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('/'); +} + +/*! + \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; + } + } + + 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(); + cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data); + cacheItem->file->open(); + cacheItem->writeHeader(cacheItem->file); + device = cacheItem->file; + } + d->inserting[device] = cacheItem; + 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); +} + +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: could'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(); + 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) || !fileName.startsWith(CACHE_PREFIX)) + 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); + QBuffer *buffer = 0; + if (!url.isValid()) + return buffer; + if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) { + buffer = new QBuffer; + buffer->setData(d->lastItem.data.data()); + } else { + QFile *file = new QFile(d->cacheFileName(url)); + if (!file->open(QFile::ReadOnly | QIODevice::Unbuffered)) { + delete file; + return 0; + } + if (!d->lastItem.read(file, true)) { + file->close(); + remove(url); + delete file; + return 0; + } + if (d->lastItem.data.isOpen()) { + // compressed + buffer = new QBuffer; + buffer->setData(d->lastItem.data.data()); + delete file; + } else { + buffer = new QBuffer; + // ### verify that QFile uses the fd size and not the file name + qint64 size = file->size() - file->pos(); + const uchar *p = 0; +#ifndef Q_OS_WINCE + p = file->map(file->pos(), size); +#endif + if (p) { + file->setParent(buffer); + buffer->setData((const char *)p, size); + } else { + buffer->setData(file->readAll()); + delete file; + } + } + } + buffer->open(QBuffer::ReadOnly); + return buffer; +} + +/*! + \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 then 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; + } + + QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot; + QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories); + + QMap<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) && fileName.startsWith(CACHE_PREFIX)) { + cacheItems[info.created()] = path; + totalSize += info.size(); + } + } + + int removedFiles = 0; + qint64 goal = (maximumCacheSize() * 9) / 10; + QMap<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 + if (removedFiles > 0) + d->lastItem.reset(); + 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; +} + +QByteArray QNetworkDiskCachePrivate::generateId(const QUrl &url) const +{ + QUrl cleanUrl = url; + cleanUrl.setPassword(QString()); + cleanUrl.setFragment(QString()); + + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(cleanUrl.toEncoded()); + return hash.result().toHex(); +} + +QString QNetworkDiskCachePrivate::tmpCacheFileName() const +{ + QDir dir; + dir.mkpath(cacheDirectory + QLatin1String("prepared/")); + return cacheDirectory + QLatin1String("prepared/") + CACHE_PREFIX + QLatin1String("XXXXXX") + CACHE_POSTFIX; +} + +QString QNetworkDiskCachePrivate::cacheFileName(const QUrl &url) const +{ + if (!url.isValid()) + return QString(); + QString directory = cacheDirectory + url.scheme() + QLatin1Char('/'); + if (!QFile::exists(directory)) { + // ### make a static QDir function for this... + QDir dir; + dir.mkpath(directory); + } + + QString fileName = CACHE_PREFIX + QLatin1String(generateId(url)) + CACHE_POSTFIX; + return directory + fileName; +} + +/*! + 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 = 7 +}; + +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); + } + return metaData.isValid(); +} + +QT_END_NAMESPACE diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h new file mode 100644 index 0000000..ca4bb94 --- /dev/null +++ b/src/network/access/qnetworkdiskcache.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKDISKCACHE_H +#define QNETWORKDISKCACHE_H + +#include <QtNetwork/qabstractnetworkcache.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Network) + +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) +}; + +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 0000000..bf6f37c --- /dev/null +++ b/src/network/access/qnetworkdiskcache_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +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) + {} + + QByteArray generateId(const QUrl &url) const; + QString cacheFileName(const QUrl &url) const; + QString tmpCacheFileName() const; + bool removeFile(const QString &file); + void storeItem(QCacheItem *item); + + mutable QCacheItem lastItem; + QString cacheDirectory; + qint64 maximumCacheSize; + qint64 currentCacheSize; + + QHash<QIODevice*, QCacheItem*> inserting; + Q_DECLARE_PUBLIC(QNetworkDiskCache) +}; + +QT_END_NAMESPACE + +#endif // QNETWORKDISKCACHE_P_H diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp new file mode 100644 index 0000000..f4dad3c --- /dev/null +++ b/src/network/access/qnetworkreply.cpp @@ -0,0 +1,691 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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) +{ + // 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 + posted with QNetworkAccessManager + + \reentrant + \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. + + \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 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 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 unknonwn 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. + + \sa QNetworkAccessManager::finished() +*/ + +/*! + \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. + + \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. + + This signal is suitable to connecting to QProgressBar::setValue() + to update the QProgressBar that provides user feedback. + + \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. + + This signal is suitable to connecting to QProgressBar::setValue() + to update the QProgressBar that provides user feedback. + + 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; +} + +/*! + 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(); +} + +/*! + 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, © }; + 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() +*/ +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 +} + +/*! + 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 0000000..6f763b3 --- /dev/null +++ b/src/network/access/qnetworkreply.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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, + UnknownNetworkError = 99, + + // proxy errors (101-199): + ProxyConnectionRefusedError = 101, + ProxyConnectionClosedError, + ProxyNotFoundError, + ProxyTimeoutError, + ProxyAuthenticationRequiredError, + UnknownProxyError = 199, + + // content errors (201-299): + ContentAccessDenied = 201, + ContentOperationNotPermittedError, + ContentNotFoundError, + AuthenticationRequiredError, + 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; + 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; + + // attributes + QVariant attribute(QNetworkRequest::Attribute code) const; + +#ifndef QT_NO_OPENSSL + QSslConfiguration sslConfiguration() const; + void setSslConfiguration(const QSslConfiguration &configuration); +#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 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 0000000..f459e85 --- /dev/null +++ b/src/network/access/qnetworkreply_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; + + 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/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp new file mode 100644 index 0000000..eaa572f --- /dev/null +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -0,0 +1,598 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate() + : copyDevice(0), networkCache(0), + cacheEnabled(false), cacheSaveDevice(0), + bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), + state(Idle) +{ +} + +void QNetworkReplyImplPrivate::_q_startOperation() +{ + // This function is called exactly once + state = Working; + if (!backend) { + error(QNetworkReplyImpl::ProtocolUnknownError, + QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!; + finished(); + return; + } + + backend->open(); + if (state != Finished) { + if (operation == QNetworkAccessManager::GetOperation) + pendingNotifications.append(NotifyDownstreamReadyWrite); + if (outgoingData) { + _q_sourceReadyRead(); +#if 0 // ### FIXME + if (outgoingData->atEndOfStream() && writeBuffer.isEmpty()) + // empty upload + emit q->uploadProgress(0, 0); +#endif + } + + handleNotifications(); + } +} + +void QNetworkReplyImplPrivate::_q_sourceReadyRead() +{ + // read data from the outgoingData QIODevice into our internal buffer + enum { DesiredBufferSize = 32 * 1024 }; + + if (writeBuffer.size() >= DesiredBufferSize) + return; // don't grow the buffer too much + + // read as many bytes are available or up until we fill up the buffer + // but always read at least one byte + qint64 bytesToRead = qBound<qint64>(1, outgoingData->bytesAvailable(), + DesiredBufferSize - writeBuffer.size()); + char *ptr = writeBuffer.reserve(bytesToRead); + qint64 bytesActuallyRead = outgoingData->read(ptr, bytesToRead); + if (bytesActuallyRead == -1) { + // EOF + writeBuffer.chop(bytesToRead); + backendNotify(NotifyCloseUpstreamChannel); + return; + } + + if (bytesActuallyRead < bytesToRead) + writeBuffer.chop(bytesToRead - bytesActuallyRead); + + // if we did read anything, let the backend know and handle it + if (bytesActuallyRead) + backendNotify(NotifyUpstreamReadyRead); + + // check for EOF again + if (!outgoingData->isSequential() && outgoingData->atEnd()) + backendNotify(NotifyCloseUpstreamChannel); +} + +void QNetworkReplyImplPrivate::_q_sourceReadChannelFinished() +{ + _q_sourceReadyRead(); +} + +void QNetworkReplyImplPrivate::_q_copyReadyRead() +{ + Q_Q(QNetworkReplyImpl); + if (!copyDevice && !q->isOpen()) + return; + + qint64 bytesToRead = nextDownstreamBlockSize(); + if (bytesToRead == 0) + // we'll be called again, eventually + return; + + bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable()); + char *ptr = readBuffer.reserve(bytesToRead); + qint64 bytesActuallyRead = copyDevice->read(ptr, bytesToRead); + if (bytesActuallyRead == -1) { + readBuffer.chop(bytesToRead); + backendNotify(NotifyCopyFinished); + return; + } + + if (bytesActuallyRead != bytesToRead) + readBuffer.chop(bytesToRead - bytesActuallyRead); + + if (!copyDevice->isSequential() && copyDevice->atEnd()) + backendNotify(NotifyCopyFinished); + + bytesDownloaded += bytesActuallyRead; + lastBytesDownloaded = bytesDownloaded; + QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); + emit q->downloadProgress(bytesDownloaded, + totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); + emit q->readyRead(); +} + +void QNetworkReplyImplPrivate::_q_copyReadChannelFinished() +{ + _q_copyReadyRead(); +} + +void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req, + QIODevice *data) +{ + Q_Q(QNetworkReplyImpl); + + outgoingData = data; + request = req; + url = request.url(); + operation = op; + + if (outgoingData) { + q->connect(outgoingData, SIGNAL(readyRead()), SLOT(_q_sourceReadyRead())); + q->connect(outgoingData, SIGNAL(readChannelFinished()), SLOT(_q_sourceReadChannelFinished())); + } + + q->QIODevice::open(QIODevice::ReadOnly); + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); +} + +void QNetworkReplyImplPrivate::setNetworkCache(QAbstractNetworkCache *nc) +{ + networkCache = nc; +} + +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() +{ + NotificationQueue current = pendingNotifications; + pendingNotifications.clear(); + + if (state != Working) + return; + + while (!current.isEmpty()) { + InternalNotifications notification = current.dequeue(); + switch (notification) { + case NotifyDownstreamReadyWrite: + if (copyDevice) + _q_copyReadyRead(); + else + backend->downstreamReadyWrite(); + break; + + case NotifyUpstreamReadyRead: + backend->upstreamReadyRead(); + break; + + case NotifyCloseDownstreamChannel: + backend->closeDownstreamChannel(); + break; + + case NotifyCloseUpstreamChannel: + backend->closeUpstreamChannel(); + break; + + case NotifyCopyFinished: { + QIODevice *dev = copyDevice; + copyDevice = 0; + backend->copyFinished(dev); + break; + } + } + } +} + +void QNetworkReplyImplPrivate::createCache() +{ + // check if we can save and if we're allowed to + if (!networkCache || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()) + 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::consume(qint64 count) +{ + Q_Q(QNetworkReplyImpl); + if (count <= 0) { + qWarning("QNetworkConnection: backend signalled that it consumed %ld bytes", long(count)); + return; + } + + if (outgoingData) + // schedule another read from the source + QMetaObject::invokeMethod(q_func(), "_q_sourceReadyRead", Qt::QueuedConnection); + + writeBuffer.skip(count); + if (bytesUploaded == -1) + bytesUploaded = count; + else + bytesUploaded += count; + + QVariant totalSize = request.header(QNetworkRequest::ContentLengthHeader); + emit q->uploadProgress(bytesUploaded, + totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); +} + +qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const +{ + enum { DesiredBufferSize = 32 * 1024 }; + if (readBufferMaxSize == 0) + return DesiredBufferSize; + + return qMax<qint64>(0, readBufferMaxSize - readBuffer.size()); +} + +void QNetworkReplyImplPrivate::feed(const QByteArray &data) +{ + Q_Q(QNetworkReplyImpl); + if (!q->isOpen()) + return; + + char *ptr = readBuffer.reserve(data.size()); + memcpy(ptr, data.constData(), data.size()); + + if (cacheEnabled && !cacheSaveDevice) { + // save the meta data + QNetworkCacheMetaData metaData; + metaData.setUrl(url); + metaData = backend->fetchCacheMetaData(metaData); + 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; + } + } + + if (cacheSaveDevice) + cacheSaveDevice->write(data); + + bytesDownloaded += data.size(); + lastBytesDownloaded = bytesDownloaded; + + QPointer<QNetworkReplyImpl> qq = q; + + QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); + emit q->downloadProgress(bytesDownloaded, + totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong()); + emit q->readyRead(); + + // hopefully we haven't been deleted here + if (!qq.isNull()) { + // do we still have room in the buffer? + if (nextDownstreamBlockSize() > 0) + backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); + } +} + +void QNetworkReplyImplPrivate::feed(QIODevice *data) +{ + Q_Q(QNetworkReplyImpl); + Q_ASSERT(q->isOpen()); + + // 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::finished() +{ + Q_Q(QNetworkReplyImpl); + Q_ASSERT_X(state != Finished, "QNetworkReplyImpl", + "Backend called finished/finishedWithError more than once"); + + state = Finished; + pendingNotifications.clear(); + + QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); + if (bytesDownloaded != lastBytesDownloaded || totalSize.isNull()) + emit q->downloadProgress(bytesDownloaded, bytesDownloaded); + if (bytesUploaded == -1 && outgoingData) + emit q->uploadProgress(0, 0); + + 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 + emit q->readChannelFinished(); + emit q->finished(); +} + +void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage) +{ + Q_Q(QNetworkReplyImpl); + + 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); + // do we have cookies? + if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()) { + 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); + if (d->isCachingEnabled()) + d->networkCache->remove(url()); +} + +void QNetworkReplyImpl::abort() +{ + Q_D(QNetworkReplyImpl); + if (d->state == QNetworkReplyImplPrivate::Aborted) + return; + + // stop both upload and download + if (d->backend) { + d->backend->deleteLater(); + d->backend = 0; + } + 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) { + // emit signals + d->error(OperationCanceledError, tr("Operation canceled")); + d->finished(); + } + d->state = QNetworkReplyImplPrivate::Aborted; +} + +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(); + + // emit signals + d->error(OperationCanceledError, tr("Operation canceled")); + d->finished(); +} + +/*! + 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 +{ + return QNetworkReply::bytesAvailable() + d_func()->readBuffer.size(); +} + +void QNetworkReplyImpl::setReadBufferSize(qint64 size) +{ + Q_D(QNetworkReplyImpl); + if (size > d->readBufferMaxSize && + size == d->readBuffer.size()) + d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); + + QNetworkReply::setReadBufferSize(size); +} + +#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(); +} + +#endif // QT_NO_OPENSSL + +/*! + \internal +*/ +qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen) +{ + Q_D(QNetworkReplyImpl); + if (d->readBuffer.isEmpty()) + return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0; + + d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite); + if (maxlen == 1) { + // optimization for getChar() + *data = d->readBuffer.getChar(); + return 1; + } + + maxlen = qMin<qint64>(maxlen, d->readBuffer.size()); + 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); +} + +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 0000000..ad06f78 --- /dev/null +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "private/qringbuffer_p.h" + +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 + virtual void close(); + virtual qint64 bytesAvailable() const; + virtual void setReadBufferSize(qint64 size); + + 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(); +#endif + + Q_DECLARE_PRIVATE(QNetworkReplyImpl) + Q_PRIVATE_SLOT(d_func(), void _q_startOperation()) + Q_PRIVATE_SLOT(d_func(), void _q_sourceReadyRead()) + Q_PRIVATE_SLOT(d_func(), void _q_sourceReadChannelFinished()) + Q_PRIVATE_SLOT(d_func(), void _q_copyReadyRead()) + Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished()) +}; + +class QNetworkReplyImplPrivate: public QNetworkReplyPrivate +{ +public: + enum InternalNotifications { + NotifyDownstreamReadyWrite, + NotifyUpstreamReadyRead, + NotifyCloseDownstreamChannel, + NotifyCloseUpstreamChannel, + NotifyCopyFinished + }; + + enum State { + Idle, + Opening, + Working, + Finished, + Aborted + }; + + typedef QQueue<InternalNotifications> NotificationQueue; + + QNetworkReplyImplPrivate(); + + void _q_startOperation(); + void _q_sourceReadyRead(); + void _q_sourceReadChannelFinished(); + void _q_copyReadyRead(); + void _q_copyReadChannelFinished(); + + void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request, + QIODevice *outgoingData); + void setNetworkCache(QAbstractNetworkCache *networkCache); + 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); + qint64 nextDownstreamBlockSize() const; + void feed(const QByteArray &data); + void feed(QIODevice *data); + 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; + QIODevice *copyDevice; + QAbstractNetworkCache *networkCache; + + bool cacheEnabled; + QIODevice *cacheSaveDevice; + + NotificationQueue pendingNotifications; + QUrl urlForLastAuthentication; +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy lastProxyAuthentication; + QList<QNetworkProxy> proxyList; +#endif + + QRingBuffer readBuffer; + QRingBuffer writeBuffer; + qint64 bytesDownloaded; + qint64 lastBytesDownloaded; + qint64 bytesUploaded; + + QString httpReasonPhrase; + int httpStatusCode; + + State state; + + Q_DECLARE_PUBLIC(QNetworkReplyImpl) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp new file mode 100644 index 0000000..56b793d --- /dev/null +++ b/src/network/access/qnetworkrequest.cpp @@ -0,0 +1,803 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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" + +QT_BEGIN_NAMESPACE + +/*! + \class QNetworkRequest + \brief The QNetworkRequest class holds one request to be sent with the Network Access API. + \since 4.4 + + \ingroup io + \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 + + 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. + + \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). Note that the default QNetworkAccessManager + implementation does not support caching, so it will ignore + this attribute. + + \value SourceIsFromCacheAttribute + Replies only, type: QVariant::Bool (default: false) + Indicates whether the data was obtained from cache + or not. + + \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) +*/ + +class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate +{ +public: + inline QNetworkRequestPrivate() +#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; + +#ifndef QT_NO_OPENSSL + if (other.sslConfiguration) + sslConfiguration = new QSslConfiguration(*other.sslConfiguration); + else + sslConfiguration = 0; +#endif + } + + inline bool operator==(const QNetworkRequestPrivate &other) const + { + return url == other.url && + rawHeaders == other.rawHeaders && + attributes == other.attributes; + // don't compare cookedHeaders + } + + QUrl url; +#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; + 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) 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 + +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"; + + // 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: + 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 + + QByteArray lower = headerName.toLower(); + switch (lower.at(0)) { + case 'c': + if (lower == "content-type") + return QNetworkRequest::ContentTypeHeader; + else if (lower == "content-length") + return QNetworkRequest::ContentLengthHeader; + else if (lower == "cookie") + return QNetworkRequest::CookieHeader; + break; + + case 'l': + if (lower == "location") + return QNetworkRequest::LocationHeader; + else if (lower == "last-modified") + return QNetworkRequest::LastModifiedHeader; + break; + + case 's': + if (lower == "set-cookie") + 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 (QByteArray cookie, cookieList) { + QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed()); + if (parsed.count() != 1) + return QVariant(); // invalid Cookie: header + + result += parsed; + } + + return qVariantFromValue(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 qVariantFromValue(QNetworkCookie::parseCookies(value)); + + default: + Q_ASSERT(0); + } + return QVariant(); +} + +QNetworkHeadersPrivate::RawHeadersList::ConstIterator +QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const +{ + QByteArray lowerKey = key.toLower(); + RawHeadersList::ConstIterator it = rawHeaders.constBegin(); + RawHeadersList::ConstIterator end = rawHeaders.constEnd(); + for ( ; it != end; ++it) + if (it->first.toLower() == lowerKey) + return it; + + return end; // not found +} + +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) +{ + QByteArray lowerKey = key.toLower(); + RawHeadersList::Iterator it = rawHeaders.begin(); + while (it != rawHeaders.end()) { + if (it->first.toLower() == lowerKey) + 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 + cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value)); + } +} + +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 { + // eat the weekday, the comma and the space following it + QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); + + QLocale c = QLocale::c(); + if (pos == 3) + // must be RFC 1123 date + dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT")); + else + // 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 0000000..6f34bce --- /dev/null +++ b/src/network/access/qnetworkrequest.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + }; + enum Attribute { + HttpStatusCodeAttribute, + HttpReasonPhraseAttribute, + RedirectionTargetAttribute, + ConnectionEncryptedAttribute, + CacheLoadControlAttribute, + CacheSaveControlAttribute, + SourceIsFromCacheAttribute, + + User = 1000, + UserMax = 32767 + }; + enum CacheLoadControl { + AlwaysNetwork, + PreferNetwork, + PreferCache, + AlwaysCache + }; + + 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 + +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 0000000..ff71f85 --- /dev/null +++ b/src/network/access/qnetworkrequest_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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" + +QT_BEGIN_NAMESPACE + +// this is the common part between QNetworkRequestPrivate and QNetworkReplyPrivate +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; + + RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) 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/kernel/kernel.pri b/src/network/kernel/kernel.pri new file mode 100644 index 0000000..8aa6ff4 --- /dev/null +++ b/src/network/kernel/kernel.pri @@ -0,0 +1,30 @@ +# 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 + +unix:SOURCES += kernel/qhostinfo_unix.cpp kernel/qnetworkinterface_unix.cpp +win32:SOURCES += kernel/qhostinfo_win.cpp kernel/qnetworkinterface_win.cpp + +mac:LIBS+= -framework SystemConfiguration +mac:SOURCES += kernel/qnetworkproxy_mac.cpp +else:win32:SOURCES += kernel/qnetworkproxy_win.cpp +else:SOURCES += kernel/qnetworkproxy_generic.cpp + diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp new file mode 100644 index 0000000..4f477bd --- /dev/null +++ b/src/network/kernel/qauthenticator.cpp @@ -0,0 +1,1026 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <qdatastream.h> +#include <qendian.h> +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +#include <../3rdparty/des/des.cpp> + +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 io + \inmodule QtNetwork + + The QAuthenticator class is usually used in the + \l{QHttp::}{authenticationRequired()} and + \l{QHttp::}{proxyAuthenticationRequired()} signals of QHttp and + QAbstractSocket. The class provides a way to pass back the required + authentication information to the socket when accessing services that + require authentication. + + \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; + detach(); + d->user = other.d->user; + d->password = other.d->password; + 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; +} + +/*! + \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(); + d->user = user; +} + +/*! + 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(); +} + + +/*! + 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) +{ + QList<QPair<QString, QString> > values = header.values(); + 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. + */ + + QString headerVal; + for (int i = 0; i < values.size(); ++i) { + const QPair<QString, QString> ¤t = values.at(i); + if (current.first.toLower() != QLatin1String(search)) + continue; + QString str = current.second; + if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) { + method = Basic; headerVal = str.mid(6); + } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) { + method = Ntlm; + headerVal = str.mid(5); + } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) { + method = DigestMd5; + headerVal = str.mid(7); + } + } + + challenge = headerVal.trimmed().toLatin1(); + QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); + + switch(method) { + case Basic: + realm = QString::fromLatin1(options.value("realm")); + if (user.isEmpty()) + phase = Done; + break; + case Ntlm: + // #### extract from header + realm = QString(); + break; + case DigestMd5: { + realm = QString::fromLatin1(options.value("realm")); + if (options.value("stale").toLower() == "true") + phase = Start; + if (user.isEmpty()) + phase = Done; + break; + } + default: + realm = QString(); + challenge = QByteArray(); + phase = Invalid; + } +} +#endif + +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 + + +/* 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; +}; + + + +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; +}; + + +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); +} + + +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; +} + + +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) { + // UNUSED right now + } + + 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; + + ctx->realm = ch.targetNameStr; + + 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)); + + offset = qEncodeNtlmString(pb.domain, offset, ctx->realm, unicode); + pb.domainStr = ctx->realm; + offset = qEncodeNtlmString(pb.user, offset, ctx->user, unicode); + pb.userStr = ctx->user; + + offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode); + pb.workstationStr = ctx->workstation; + + // Get LM response + pb.lmResponseBuf = qEncodeLmResponse(ctx, ch); + offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf); + + // Get NTLM response + pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch); + 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 0000000..aa78a56 --- /dev/null +++ b/src/network/kernel/qauthenticator.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUTHENTICATOR_H +#define QAUTHENTICATOR_H + +#include <QtCore/qstring.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; + + 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 0000000..42583cf --- /dev/null +++ b/src/network/kernel/qauthenticator_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +QT_BEGIN_NAMESPACE + +class QHttpResponseHeader; + +class QAuthenticatorPrivate +{ +public: + enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 }; + QAuthenticatorPrivate(); + + QAtomicInt ref; + QString user; + QString password; + QHash<QByteArray, QByteArray> 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; + + 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 + +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp new file mode 100644 index 0000000..fc43369 --- /dev/null +++ b/src/network/kernel/qhostaddress.cpp @@ -0,0 +1,1166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 io + \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(), checked for its type + using isIPv4Address() or isIPv6Address(), and retrieved with + toIPv4Address(), toIPv6Address(), or toString(). + + The class also supports common predefined addresses: \l Null, \l + LocalHost, \l LocalHostIPv6, \l Broadcast, and \l Any. + + \sa 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)) +{ +} + +/*! + 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() +{ + delete d; +} + +/*! + Assigns another host \a address to this object, and returns a reference + to this object. +*/ +QHostAddress &QHostAddress::operator=(const QHostAddress &address) +{ + *d = *address.d; + 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 isIp4Addr() returns true. + + \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 isIPv6Address() returns true. + + \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 {Format of the QDataStream operators} +*/ +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 {Format of the QDataStream operators} +*/ +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 0000000..02a6f97 --- /dev/null +++ b/src/network/kernel/qhostaddress.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHOSTADDRESS_H +#define QHOSTADDRESS_H + +#include <QtCore/qpair.h> +#include <QtCore/qstring.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 ©); + 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: + 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 0000000..e6634b7 --- /dev/null +++ b/src/network/kernel/qhostaddress_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..ca4124d --- /dev/null +++ b/src/network/kernel/qhostinfo.cpp @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhostinfo.h" +#include "qhostinfo_p.h" + +#include <qabstracteventdispatcher.h> +#include <private/qunicodetables_p.h> +#include <qcoreapplication.h> +#include <qmetaobject.h> +#include <qregexp.h> +#include <private/qnativesocketengine_p.h> +#include <qstringlist.h> +#include <qthread.h> +#include <qtimer.h> +#include <qurl.h> + +#ifdef Q_OS_UNIX +# include <unistd.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QHostInfoAgent, theAgent) +void QHostInfoAgent::staticCleanup() +{ + theAgent()->cleanup(); +} + +//#define QHOSTINFO_DEBUG + +/*! + \class QHostInfo + \brief The QHostInfo class provides static functions for host name lookups. + + \reentrant + \inmodule QtNetwork + \ingroup io + + 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. (If you use + Qt for Embedded Linux and disabled multithreading support by defining + \c QT_NO_THREAD, lookupHost() will block until the lookup has + finished.) The results are stored in a QHostInfo object. Call + 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. + + \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 + + \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"); + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + QWindowsSockInit bust; // makes sure WSAStartup was callled +#endif + + // Support for IDNA + QString lookup = QString::fromLatin1(QUrl::toAce(name)); + + QHostInfoResult *result = new QHostInfoResult; + result->autoDelete = false; + QObject::connect(result, SIGNAL(resultsReady(QHostInfo)), + receiver, member); + int id = result->lookupId = theIdCounter.fetchAndAddRelaxed(1); + + if (lookup.isEmpty()) { + QHostInfo info(id); + info.setError(QHostInfo::HostNotFound); + info.setErrorString(QObject::tr("No host name given")); + QMetaObject::invokeMethod(result, "emitResultsReady", Qt::QueuedConnection, + Q_ARG(QHostInfo, info)); + result->autoDelete = true; + return id; + } + + QHostInfoAgent *agent = theAgent(); + agent->addHostName(lookup, result); + +#if !defined QT_NO_THREAD + if (!agent->isRunning()) + agent->start(); +#else +// if (!agent->isRunning()) + agent->run(); +// else +// agent->wakeOne(); +#endif + return id; +} + +/*! + Aborts the host lookup with the ID \a id, as returned by lookupHost(). + + \sa lookupHost(), lookupId() +*/ +void QHostInfo::abortHostLookup(int id) +{ + QHostInfoAgent *agent = theAgent(); + agent->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 + + if (!name.isEmpty()) + return QHostInfoAgent::fromName(QLatin1String(QUrl::toAce(name))); + + QHostInfo retval; + retval.d->err = HostNotFound; + retval.d->errorStr = QObject::tr("No host name given"); + return retval; +} + +/*! + \internal + Pops a query off the queries list, performs a blocking call to + QHostInfoAgent::lookupHost(), and emits the resultsReady() + signal. This process repeats until the queries list is empty. +*/ +void QHostInfoAgent::run() +{ +#ifndef QT_NO_THREAD + // Dont' allow thread termination during event delivery, but allow it + // during the actual blocking host lookup stage. + setTerminationEnabled(false); + forever +#endif + { + QHostInfoQuery *query; + { +#ifndef QT_NO_THREAD + // the queries list is shared between threads. lock all + // access to it. + QMutexLocker locker(&mutex); + if (!quit && queries.isEmpty()) + cond.wait(&mutex); + if (quit) { + // Reset the quit variable in case QCoreApplication is + // destroyed and recreated. + quit = false; + break; + } + if (queries.isEmpty()) + continue; +#else + if (queries.isEmpty()) + return; +#endif + query = queries.takeFirst(); + pendingQueryId = query->object->lookupId; + } + +#if defined(QHOSTINFO_DEBUG) + qDebug("QHostInfoAgent::run(%p): looking up \"%s\"", this, + query->hostName.toLatin1().constData()); +#endif + +#ifndef QT_NO_THREAD + // Start query - allow termination at this point, but not outside. We + // don't want to all termination during event delivery, but we don't + // want the lookup to prevent the app from quitting (the agent + // destructor terminates the thread). + setTerminationEnabled(true); +#endif + QHostInfo info = fromName(query->hostName); +#ifndef QT_NO_THREAD + setTerminationEnabled(false); +#endif + + int id = query->object->lookupId; + info.setLookupId(id); + if (pendingQueryId == id) + query->object->emitResultsReady(info); + delete query; + } +} + +/*! + \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)) +{ +} + +/*! + 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 = *other.d; + return *this; +} + +/*! + Destroys the host info object. +*/ +QHostInfo::~QHostInfo() +{ + delete d; +} + +/*! + 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() +*/ + +QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h new file mode 100644 index 0000000..084ec89 --- /dev/null +++ b/src/network/kernel/qhostinfo.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHOSTINFO_H +#define QHOSTINFO_H + +#include <QtCore/qlist.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: + 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 0000000..1db00d3 --- /dev/null +++ b/src/network/kernel/qhostinfo_p.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 QLibrary 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" + +#if !defined QT_NO_THREAD +#include "QtCore/qthread.h" +# define QHostInfoAgentBase QThread +#else +# define QHostInfoAgentBase QObject +#endif + +QT_BEGIN_NAMESPACE + +static const int QHOSTINFO_THREAD_WAIT = 250; // ms + +class QHostInfoResult : public QObject +{ + Q_OBJECT +public Q_SLOTS: + inline void emitResultsReady(const QHostInfo &info) + { + emit resultsReady(info); + if (autoDelete) + delete this; + } + +Q_SIGNALS: + void resultsReady(const QHostInfo &info); + +public: + int lookupId; + bool autoDelete; +}; + +struct QHostInfoQuery +{ + inline QHostInfoQuery() : object(0) {} + inline ~QHostInfoQuery() { delete object; } + inline QHostInfoQuery(const QString &name, QHostInfoResult *result) + : hostName(name), object(result) {} + + QString hostName; + QHostInfoResult *object; +}; + +class QHostInfoAgent : public QHostInfoAgentBase +{ + Q_OBJECT +public: + inline QHostInfoAgent() + { + // There is a chance that there will be two instances of + // QHostInfoAgent if two threads try to get Q_GLOBAL_STATIC + // object at the same time. The second object will be deleted + // immediately before anyone uses it, but we need to be + // careful about what we do in the constructor. + static QBasicAtomicInt done = Q_BASIC_ATOMIC_INITIALIZER(0); + if (done.testAndSetRelaxed(0, 1)) + qAddPostRoutine(staticCleanup); + moveToThread(QCoreApplicationPrivate::mainThread()); + quit = false; + pendingQueryId = -1; + } + inline ~QHostInfoAgent() + { cleanup(); } + + void run(); + static QHostInfo fromName(const QString &hostName); + + inline void addHostName(const QString &name, QHostInfoResult *result) + { + QMutexLocker locker(&mutex); + queries << new QHostInfoQuery(name, result); + cond.wakeOne(); + } + + inline void abortLookup(int id) + { + QMutexLocker locker(&mutex); + for (int i = 0; i < queries.size(); ++i) { + QHostInfoResult *result = queries.at(i)->object; + if (result->lookupId == id) { + result->disconnect(); + delete queries.takeAt(i); + return; + } + } + if (pendingQueryId == id) + pendingQueryId = -1; + } + + static void staticCleanup(); + +public Q_SLOTS: + inline void cleanup() + { + { + QMutexLocker locker(&mutex); + qDeleteAll(queries); + queries.clear(); + quit = true; + cond.wakeOne(); + } +#ifndef QT_NO_THREAD + if (!wait(QHOSTINFO_THREAD_WAIT)) + terminate(); + wait(); +#endif + } + +private: + QList<QHostInfoQuery *> queries; + QMutex mutex; + QWaitCondition cond; + volatile bool quit; + int pendingQueryId; +}; + +class QHostInfoPrivate +{ +public: + inline QHostInfoPrivate() + : err(QHostInfo::NoError), + errorStr(QLatin1String(QT_TRANSLATE_NOOP("QHostInfo", "Unknown error"))) + { + } + + QHostInfo::HostInfoError err; + QString errorStr; + QList<QHostAddress> addrs; + QString hostName; + int lookupId; +}; + +QT_END_NAMESPACE + +#endif // QHOSTINFO_P_H diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp new file mode 100644 index 0000000..f987520 --- /dev/null +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QHOSTINFO_DEBUG + +static const int RESOLVER_TIMEOUT = 2000; + +#include "qplatformdefs.h" + +#include "qhostinfo_p.h" +#include "qiodevice.h" +#include <qbytearray.h> +#include <qlibrary.h> +#include <qurl.h> +#include <qfile.h> +#include <private/qmutexpool_p.h> + +extern "C" { +#include <sys/types.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <resolv.h> +} + +#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; + results.setHostName(hostName); + +#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) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&local_res_init)); +#endif + 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.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(hbuf)); +#else + in_addr_t inetaddr = inet_addr(hostName.toLatin1().constData()); + struct hostent *ent = gethostbyaddr((const char *)&inetaddr, sizeof(inetaddr), AF_INET); + if (!ent) { + results.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(ent->h_name)); +#endif + } + +#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(hostName.toLatin1().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(hostName.toLatin1().constData(), 0, &hints, &res); + } +# endif + + if (result == 0) { + addrinfo *node = res; + QList<QHostAddress> addresses; + while (node) { + 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; + addr.setAddress(((sockaddr_in6 *) node->ai_addr)->sin6_addr.s6_addr); + 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(hostName.toLatin1().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")); + } + } 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")); + } 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() +{ + resolveLibrary(); + if (local_res_ninit) { + // using thread-safe version + res_state_ptr state = res_state_ptr(qMalloc(sizeof(*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; + } + + // 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 0000000..0a34e2b --- /dev/null +++ b/src/network/kernel/qhostinfo_win.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined Q_CC_MSVC && _MSC_VER <=1300 +//VC.net 2002 support for templates doesn't match some PSDK requirements +#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0])) +#endif + +#include <winsock2.h> + +#include "qhostinfo_p.h" +#include "private/qnativesocketengine_p.h" +#include <ws2tcpip.h> +#include <qlibrary.h> +#include <qtimer.h> +#include <qmutex.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; +}; + +// 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 { + uchar qt_s6_addr[16]; +}; + +struct qt_sockaddr_in6 { + 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 */ +}; + +//### +#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) QLibrary::resolve(QLatin1String("ws2_32.dll"), "getaddrinfo"); + local_freeaddrinfo = (freeaddrinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "freeaddrinfo"); + local_getnameinfo = (getnameinfoProto) QLibrary::resolve(QLatin1String("ws2_32.dll"), "getnameinfo"); +#else + local_getaddrinfo = (getaddrinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "getaddrinfo"); + local_freeaddrinfo = (freeaddrinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "freeaddrinfo"); + local_getnameinfo = (getnameinfoProto) QLibrary::resolve(QLatin1String("ws2.dll"), "getnameinfo"); +#endif +} + +#if defined(Q_OS_WINCE) +#include <qmutex.h> +QMutex qPrivCEMutex; +#endif +/* + Performs a blocking call to gethostbyname or getaddrinfo, stores + the results in a QHostInfo structure and emits the + resultsReady() signal. +*/ +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) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&local_getaddrinfo)); +#endif + if (!triedResolve) { + resolveLibrary(); + triedResolve = true; + } + } + + QHostInfo results; + results.setHostName(hostName); + +#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.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + 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.setError(QHostInfo::HostNotFound); + results.setErrorString(tr("Host not found")); + return results; + } + results.setHostName(QString::fromLatin1(ent->h_name)); + } + } + + 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(hostName.toLatin1().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(hostName.toLatin1().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 0000000..670745b --- /dev/null +++ b/src/network/kernel/qnetworkinterface.cpp @@ -0,0 +1,615 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 io + + 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)) +{ +} + +/*! + Makes a copy of the QNetworkAddressEntry object \a other. +*/ +QNetworkAddressEntry &QNetworkAddressEntry::operator=(const QNetworkAddressEntry &other) +{ + *d = *other.d; + return *this; +} + +/*! + Destroys this QNetworkAddressEntry object. +*/ +QNetworkAddressEntry::~QNetworkAddressEntry() +{ + delete d; +} + +/*! + 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 io + + 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 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 (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()); + debug.nospace() << ", entries = " << networkInterface.addressEntries() + << ")\n"; + 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 0000000..09fbd0f --- /dev/null +++ b/src/network/kernel/qnetworkinterface.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNETWORKINTERFACE_H +#define QNETWORKINTERFACE_H + +#include <QtCore/qshareddata.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: + 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 0000000..c07e23c --- /dev/null +++ b/src/network/kernel/qnetworkinterface_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp new file mode 100644 index 0000000..34a44ac --- /dev/null +++ b/src/network/kernel/qnetworkinterface_unix.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qset.h" +#include "qnetworkinterface.h" +#include "qnetworkinterface_p.h" +#include "qalgorithms.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 (::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 (::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 (::ioctl(socket, SIOCGIFFLAGS, &req) >= 0) { + iface->flags = convertFlags(req.ifr_flags); + } + +#ifdef SIOCGIFHWADDR + // Get the HW address + if (::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 = ::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 (::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 (::ioctl(socket, SIOCGIFNETMASK, &req) >= 0) { + sockaddr *sa = &req.ifr_addr; + entry.setNetmask(addressFromSockaddr(sa)); + } + + // Get the address of the interface + if (::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 = ::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 0000000..9540c18 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_win.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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> + +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; + QT_WA({ + iphlpapiHnd = LoadLibraryW(L"iphlpapi"); + }, { + iphlpapiHnd = LoadLibraryA("iphlpapi"); + }); + if (iphlpapiHnd == NULL) + return; // failed to load, probably Windows 95 + +#if defined(Q_OS_WINCE) + ptrGetAdaptersInfo = (PtrGetAdaptersInfo)GetProcAddressW(iphlpapiHnd, L"GetAdaptersInfo"); + ptrGetAdaptersAddresses = (PtrGetAdaptersAddresses)GetProcAddressW(iphlpapiHnd, L"GetAdaptersAddresses"); + ptrGetNetworkParams = (PtrGetNetworkParams)GetProcAddressW(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); + // 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); + + // 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)) + 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); + + // 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); + + // 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 0000000..07425d0 --- /dev/null +++ b/src/network/kernel/qnetworkinterface_win_p.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <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 0000000..f4ece97 --- /dev/null +++ b/src/network/kernel/qnetworkproxy.cpp @@ -0,0 +1,1255 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QNetworkProxy + + \since 4.1 + + \brief The QNetworkProxy class provides a network layer proxy. + + \reentrant + \ingroup io + \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, + QHttp 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, but domain name resolution via the SOCKS server is not + supported; i.e. all domain names are resolved locally. 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 QHttp, + 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/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)), + }; + + Q_ASSERT(int(type) >= 0 && int(type) <= int(QNetworkProxy::FtpCachingProxy)); + 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; + + 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) + { } + + 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) +{ + globalNetworkProxy()->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)) +{ + globalNetworkProxy()->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. + + \sa type(), setCapabilities() +*/ +void QNetworkProxy::setType(QNetworkProxy::ProxyType type) +{ + d->type = 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; +} + +/*! + \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 io + \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. +*/ + +/*! + 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() +{ +} + +/*! + 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. + + On MacOS X, this function will ignore the Proxy Auto Configuration + settings, since it cannot execute the associated ECMAScript code. +*/ + +/*! + 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); +} + +#endif // QT_NO_NETWORKPROXY + +QT_END_NAMESPACE diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h new file mode 100644 index 0000000..abe4213 --- /dev/null +++ b/src/network/kernel/qnetworkproxy.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 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 0000000..866d63d --- /dev/null +++ b/src/network/kernel/qnetworkproxy_generic.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..5e6e3e8 --- /dev/null +++ b/src/network/kernel/qnetworkproxy_mac.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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())) + 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); + + // ### Use PAC somehow + qDebug("Mac system proxy: found PAC script at \"%s\"", qPrintable(url)); + } + } + + // 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; + } + + 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_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp new file mode 100644 index 0000000..16d18cf --- /dev/null +++ b/src/network/kernel/qnetworkproxy_win.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnetworkproxy.h" + +#ifndef QT_NO_NETWORKPROXY + +#if defined(UNICODE) + +#include <qmutex.h> +#include <qstringlist.h> +#include <qregexp.h> +#include <qurl.h> + +#include <string.h> +#include <windows.h> +#include <wininet.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 + +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; + if (QSysInfo::windowsVersion() & QSysInfo::WV_DOS_based) + return; // no point, this library is only available on 2k, XP and up + +#ifdef Q_OS_WINCE + // Windows CE does not have any of the following API + return; +#else + // load the winhttp.dll library + HINSTANCE winhttpHnd = LoadLibraryW(L"winhttp"); + if (!winhttpHnd) + return; // failed to load + + ptrWinHttpOpen = (PtrWinHttpOpen)GetProcAddress(winhttpHnd, "WinHttpOpen"); + ptrWinHttpCloseHandle = (PtrWinHttpCloseHandle)GetProcAddress(winhttpHnd, "WinHttpCloseHandle"); + ptrWinHttpGetProxyForUrl = (PtrWinHttpGetProxyForUrl)GetProcAddress(winhttpHnd, "WinHttpGetProxyForUrl"); + ptrWinHttpGetDefaultProxyConfiguration = (PtrWinHttpGetDefaultProxyConfiguration)GetProcAddress(winhttpHnd, "WinHttpGetDefaultProxyConfiguration"); + ptrWinHttpGetIEProxyConfigForCurrentUser = (PtrWinHttpGetIEProxyConfigForCurrentUser)GetProcAddress(winhttpHnd, "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) { + proxyServerList << 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 = true; + 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, if we have a URL + QUrl url = query.url(); + if (query.queryType() != QNetworkProxyQuery::UrlRequest) { + // change the scheme to https, maybe it'll work + url.setScheme(QLatin1String("https")); + } + if (ptrWinHttpGetProxyForUrl(sp->hHttpSession, + (LPCWSTR)url.toString().utf16(), + &sp->autoProxyOptions, + &proxyInfo)) { + // 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 + return sp->defaultResult; + } + + // static configuration + if (isBypassed(query.peerHostName(), sp->proxyBypass)) + return sp->defaultResult; + + return parseServerList(query, sp->proxyServerList); +} + +#else // !UNICODE + +QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &) +{ + return QList<QNetworkProxy>() << QNetworkProxy::NoProxy; +} + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/network/kernel/qurlinfo.cpp b/src/network/kernel/qurlinfo.cpp new file mode 100644 index 0000000..255c9ea --- /dev/null +++ b/src/network/kernel/qurlinfo.cpp @@ -0,0 +1,731 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 misc + + 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://doc.trolltech.com/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; +} + +#endif // QT_NO_URLINFO + +QT_END_NAMESPACE diff --git a/src/network/kernel/qurlinfo.h b/src/network/kernel/qurlinfo.h new file mode 100644 index 0000000..502fd34 --- /dev/null +++ b/src/network/kernel/qurlinfo.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..d476b56 --- /dev/null +++ b/src/network/network.pro @@ -0,0 +1,17 @@ +# Qt network module + +TARGET = QtNetwork +QPRO_PWD = $$PWD +DEFINES += QT_BUILD_NETWORK_LIB QT_NO_USING_NAMESPACE +QT = core +win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x64000000 + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore + +include(../qbase.pri) +include(access/access.pri) +include(kernel/kernel.pri) +include(socket/socket.pri) +include(ssl/ssl.pri) + +QMAKE_LIBS += $$QMAKE_LIBS_NETWORK diff --git a/src/network/network.qrc b/src/network/network.qrc new file mode 100644 index 0000000..06f98cb --- /dev/null +++ b/src/network/network.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/trolltech/network"> +<file>ssl/qt-ca-bundle.crt</file> +</qresource> +</RCC> diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp new file mode 100644 index 0000000..910e30a --- /dev/null +++ b/src/network/socket/qabstractsocket.cpp @@ -0,0 +1,2631 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 io + \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. + + 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, QHttp, QTcpServer +*/ + +/*! + \fn void QAbstractSocket::hostFound() + + This signal is emitted after connectToHost() has been called and + the host lookup has succeeded. + + \sa connected() +*/ + +/*! + \fn void QAbstractSocket::connected() + + This signal is emitted after connectToHost() has been called and + a connection has been successfully established. + + \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_REGISTER_METATYPE. + + \sa error(), errorString() +*/ + +/*! + \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. + + \sa state() +*/ + +/*! + \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() +*/ + +#include "qabstractsocket.h" +#include "qabstractsocket_p.h" + +#include <qabstracteventdispatcher.h> +#include <qdatetime.h> +#include <qhostaddress.h> +#include <qhostinfo.h> +#include <qmetaobject.h> +#include <qpointer.h> +#include <qtimer.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), + connectTimeElapsed(0), + hostLookupId(-1), + 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(); + } +} + +/*! \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 = "TcpSocket"; + else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = "UdpSocket"; + else typeStr = "UnknownSocketType"; + QString protocolStr; + if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = "IPv4Protocol"; + else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = "IPv6Protocol"; + else protocolStr = "UnknownNetworkLayerProtocol"; +#endif + + resetSocketLayer(); + socketEngine = QAbstractSocketEngine::createSocketEngine(q->socketType(), proxyInUse, q); + 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); + } + } + 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 + readSocketNotifierCalled = false; + 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(); + readSocketNotifierCalled = false; + 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 && socketEngine && socketEngine->hasPendingDatagrams()) +#endif + ; + + if (!emittedReadyRead && hasData) { + emittedReadyRead = true; + emit q->readyRead(); + emittedReadyRead = false; + } + + // 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 + readSocketNotifierCalled = false; + 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; + } + readSocketNotifierCalled = 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->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()) { +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocketPrivate::flush() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s", + socketEngine->isValid() ? "yes" : "no", writeBuffer.isEmpty() ? "yes" : "no"); +#endif + 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()); + emit q->error(socketError); + // an unexpected error so close the socket. +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug() << "QAbstractSocketPrivate::flush() write error, aborting." << socketEngine->errorString(); +#endif + 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) { + emittedBytesWritten = true; + emit q->bytesWritten(written); + emittedBytesWritten = false; + } + } + + if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()) + 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; + + addresses = hostInfo.addresses(); + +#if defined(QABSTRACTSOCKET_DEBUG) + QString s = "{"; + for (int i = 0; i < addresses.count(); ++i) { + if (i != 0) s += ", "; + s += addresses.at(i).toString(); + } + s += "}"; + 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(); + } +} + +/*! \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(); +#ifdef Q_OS_LINUX + if (bytesToRead > 0) // ### See setSocketDescriptor() + bytesToRead += addToBytesAvailable; +#endif + if (bytesToRead == 0) { + // Under heavy load, certain conditions can trigger read notifications + // for socket notifiers on which there is no activity. If we continue + // 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 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 +} + +/*! \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., + "qtsoftware.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) { + qWarning("QAbstractSocket::connectToHost() called when already 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; +#ifdef Q_OS_LINUX + // ### See setSocketDescriptor(). + d->addToBytesAvailable = 0; +#endif + 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 (!d_func()->isBuffered) + openMode |= QAbstractSocket::Unbuffered; + 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) + d->hostLookupId = QHostInfo::lookupHost(hostName, this, SLOT(_q_startConnecting(QHostInfo))); + } + +#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(); + if (d->isBuffered) + available += (qint64) d->readBuffer.size(); + else if (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); + 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; + +#ifdef Q_OS_LINUX + // ### This is a workaround for certain broken Linux kernels, when using + // QTcpSocket with a Unix domain socket. It was introduced around 2.6.9, + // and fixed at some point after that. + // http://archive.linux-usenet.com/index-t-73300.html + // We can provide a better workaround for this: readFromSocket() can loop + // while reading, but this must happen without triggering an implicit + // close because of reading after the socket has closed. + d->addToBytesAvailable = 4096; +#endif + + return true; +} + +/* + 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. + + \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; + QTime 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; + d->_q_startConnecting(QHostInfo::fromName(d->hostName)); + } + if (state() == UnconnectedState) + return false; + + 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 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 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; + } + + QTime 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; + + QTime 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; + } + + QTime 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); + if (d->socketEngine && !d->socketEngine->isReadNotificationEnabled() && d->socketEngine->isValid()) + d->socketEngine->setReadNotificationEnabled(true); + + if (!d->isBuffered) { + if (!d->socketEngine) + return -1; // no socket engine is probably EOF + qint64 readBytes = d->socketEngine->read(data, maxSize); + if (readBytes < 0) { + d->socketError = d->socketEngine->error(); + setErrorString(d->socketEngine->errorString()); + } + if (!d->socketEngine->isReadNotificationEnabled()) + d->socketEngine->setReadNotificationEnabled(true); +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", + data, qt_prettyDebug(data, 32, readBytes).data(), maxSize, + readBytes); +#endif + return readBytes; + } + + if (d->readBuffer.isEmpty()) + // if we're still connected, return 0 indicating there may be more data in the future + // if we're not connected, return -1 indicating EOF + return d->state == QAbstractSocket::ConnectedState ? qint64(0) : qint64(-1); + + // If readFromSocket() read data, copy it to its destination. + if (maxSize == 1) { + *data = d->readBuffer.getChar(); +#if defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p '%c (0x%.2x)', 1) == 1", + data, isprint(int(uchar(*data))) ? *data : '?', *data); +#endif + return 1; + } + + 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 defined (QABSTRACTSOCKET_DEBUG) + qDebug("QAbstractSocket::readData(%p \"%s\", %lli) == %lld", + 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) { + 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; + } + + 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; +} + +/*! + Disconnects the socket's connection with the host. + + \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 + } 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->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 || 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->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 0000000..1b86e92 --- /dev/null +++ b/src/network/socket/qabstractsocket.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 +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 + }; + + 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); + + 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()) + +#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 0000000..4cb7dcb --- /dev/null +++ b/src/network/socket/qabstractsocket_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 QLibrary 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/qnativesocketengine_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(); + + 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(); + +#ifdef Q_OS_LINUX + qint64 addToBytesAvailable; +#endif + qint64 readBufferMaxSize; + QRingBuffer readBuffer; + QRingBuffer writeBuffer; + + bool isBuffered; + int blockingTimeout; + + QTimer *connectTimer; + int connectTimeElapsed; + + int hostLookupId; + + QAbstractSocket::SocketType socketType; + QAbstractSocket::SocketState state; + + QAbstractSocket::SocketError socketError; +}; + +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 0000000..620cf8b --- /dev/null +++ b/src/network/socket/qabstractsocketengine.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qabstractsocketengine_p.h" +#include "qnativesocketengine_p.h" +#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 + + return new QNativeSocketEngine(parent); +} + +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; + } + return new QNativeSocketEngine(parent); +} + +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 0000000..6bfa456 --- /dev/null +++ b/src/network/socket/qabstractsocketengine_p.h @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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; +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 + }; + + 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 + 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 + + 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 0000000..540c443 --- /dev/null +++ b/src/network/socket/qhttpsocketengine.cpp @@ -0,0 +1,773 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttpsocketengine_p.h" +#include "qtcpsocket.h" +#include "qhostaddress.h" +#include "qdatetime.h" +#include "qurl.h" +#include "qhttp.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); + + // 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 = 0; + + if (!d->readBuffer.isEmpty()) { + // Read as much from the buffer as we can. + bytesRead = qMin((qint64)d->readBuffer.size(), maxlen); + memcpy(data, d->readBuffer.constData(), bytesRead); + data += bytesRead; + maxlen -= bytesRead; + d->readBuffer = d->readBuffer.mid(bytesRead); + } + + qint64 bytesReadFromSocket = d->socket->read(data, maxlen); + + if (d->socket->state() == QAbstractSocket::UnconnectedState + && d->socket->bytesAvailable() == 0) { + emitReadNotification(); + } + + if (bytesReadFromSocket > 0) { + // Add to what we read so far. + bytesRead += bytesReadFromSocket; + } else if (bytesRead == 0 && bytesReadFromSocket == -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 +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 + +int QHttpSocketEngine::option(SocketOption) const +{ + return -1; +} + +bool QHttpSocketEngine::setOption(SocketOption, int) +{ + 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; + + QTime 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; + } + + QTime 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" + "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(); + + 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) { + emitReadNotification(); + } else { + qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error; + } +} + +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 (d->readNotificationEnabled && !d->readNotificationPending) { + 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) +{ + 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; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h new file mode 100644 index 0000000..d0c53d0 --- /dev/null +++ b/src/network/socket/qhttpsocketengine_p.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + 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 + + 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; + 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 0000000..d6b1507 --- /dev/null +++ b/src/network/socket/qlocalserver.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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. + + Note that this feature is not supported on Windows 9x. + + \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 = QString(); + d->fullServerName = QString(); + d->errorString = QString(); + 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 = QString(); + d->fullServerName = QString(); + 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 Q_OS_WIN + d->socketNotifier->setEnabled(d->pendingConnections.size() + <= d->maxPendingConnections); +#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 0000000..8e8babd --- /dev/null +++ b/src/network/socket/qlocalserver.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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) +#if defined(QT_LOCALSOCKET_TCP) + Q_PRIVATE_SLOT(d_func(), void _q_onNewConnection()) +#elif defined(Q_OS_WIN) + Q_PRIVATE_SLOT(d_func(), void _q_openSocket(HANDLE handle)) + Q_PRIVATE_SLOT(d_func(), void _q_stoppedListening()) + Q_PRIVATE_SLOT(d_func(), void _q_setError(QAbstractSocket::SocketError error, const QString &errorString)) +#else + Q_PRIVATE_SLOT(d_func(), void _q_socketActivated()) +#endif +}; + +#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 0000000..8e96401 --- /dev/null +++ b/src/network/socket/qlocalserver_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <qthread.h> +#else +# include <private/qnativesocketengine_p.h> +# include <qsocketnotifier.h> +#endif + +QT_BEGIN_NAMESPACE + +#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) + +/*! + \internal + QLocalServerThread exists because Windows does not have a + way to provide notifications when there is a new connections to + the server. + */ +class QLocalServerThread : public QThread +{ + Q_OBJECT + +Q_SIGNALS: + void connected(HANDLE newSocket); + void error(QAbstractSocket::SocketError error, const QString &errorString); + +public: + QLocalServerThread(QObject *parent = 0); + ~QLocalServerThread(); + void closeServer(); + +public: + QString setName(const QString &name); + void run(); + void stop(); + bool makeHandle(); + + HANDLE gotConnectionEvent; + QQueue<HANDLE> pendingHandles; + int maxPendingConnections; +private: + HANDLE stopEvent; + QString fullServerName; +}; + +#endif + +class QLocalServerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QLocalServer) + +public: + QLocalServerPrivate() : +#if defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) + inWaitingFunction(false), +#elif !defined(QT_LOCALSOCKET_TCP) + 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); + +#if defined(QT_LOCALSOCKET_TCP) + void _q_onNewConnection(); + + QTcpServer tcpServer; + QMap<quintptr, QTcpSocket*> socketMap; +#elif defined(Q_OS_WIN) + void _q_openSocket(HANDLE socket); + void _q_stoppedListening(); + void _q_setError(QAbstractSocket::SocketError error, const QString &errorString); + + QLocalServerThread waitForConnection; + bool inWaitingFunction; +#else + void setError(const QString &function); + void _q_socketActivated(); + + 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 0000000..f85777b --- /dev/null +++ b/src/network/socket/qlocalserver_tcp.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + *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 0000000..065a9de --- /dev/null +++ b/src/network/socket/qlocalserver_unix.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocalserver.h" +#include "qlocalserver_p.h" +#include "qlocalsocket.h" +#include "qlocalsocket_p.h" + +#ifndef QT_NO_LOCALSERVER + +#include <sys/socket.h> +#include <sys/un.h> + +#include <qdebug.h> +#include <qdir.h> +#include <qdatetime.h> + +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 = qSocket(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); + + // bind + if(-1 == qBind(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 == qListen(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_socketActivated())); + socketNotifier->setEnabled(maxPendingConnections > 0); + return true; +} + +/*! + \internal + + \sa QLocalServer::closeServer() + */ +void QLocalServerPrivate::closeServer() +{ + if (-1 != listenSocket) + QT_CLOSE(listenSocket); + listenSocket = -1; + + if (socketNotifier) + 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_socketActivated() +{ + Q_Q(QLocalServer); + if (-1 == listenSocket) + return; + + ::sockaddr_un addr; + QT_SOCKLEN_T length = sizeof(sockaddr_un); + int connectedSocket = qAccept(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; + + // 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. + QTime timer; + timer.start(); + while (pendingConnections.isEmpty() && (-1 == msec || timer.elapsed() < msec)) { + result = ::select(listenSocket + 1, &readfds, 0, 0, &timeout); + if (-1 == result && errno != EINTR) { + setError(QLatin1String("QLocalServer::waitForNewConnection")); + closeServer(); + break; + } + if (result > 0) + _q_socketActivated(); + } + 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 0000000..880cd7e --- /dev/null +++ b/src/network/socket/qlocalserver_win.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocalserver.h" +#include "qlocalserver_p.h" +#include "qlocalsocket.h" + +#include <qdebug.h> +#include <qdatetime.h> +#include <qcoreapplication.h> +#include <QMetaType> + +// 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 + +QT_BEGIN_NAMESPACE + +QLocalServerThread::QLocalServerThread(QObject *parent) : QThread(parent), + maxPendingConnections(1) +{ + stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + gotConnectionEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +QLocalServerThread::~QLocalServerThread() +{ + stop(); + closeServer(); + CloseHandle(stopEvent); + CloseHandle(gotConnectionEvent); +} + +void QLocalServerThread::stop() +{ + if (isRunning()) { + SetEvent(stopEvent); + wait(); + ResetEvent(stopEvent); + } +} + +void QLocalServerThread::closeServer() +{ + while (!pendingHandles.isEmpty()) + CloseHandle(pendingHandles.dequeue()); +} + +QString QLocalServerThread::setName(const QString &name) +{ + QString pipePath = QLatin1String("\\\\.\\pipe\\"); + if (name.startsWith(pipePath)) + fullServerName = name; + else + fullServerName = pipePath + name; + for (int i = pendingHandles.count(); i < maxPendingConnections; ++i) + if (!makeHandle()) + break; + return fullServerName; +} + +bool QLocalServerThread::makeHandle() +{ + if (pendingHandles.count() >= maxPendingConnections) + return false; + + HANDLE handle = INVALID_HANDLE_VALUE; + QT_WA({ + handle = CreateNamedPipeW( + (TCHAR*)fullServerName.utf16(), // pipe name + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access + PIPE_TYPE_MESSAGE | // message type pipe + PIPE_READMODE_MESSAGE | // message-read mode + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + BUFSIZE, // output buffer size + BUFSIZE, // input buffer size + 3000, // client time-out + NULL); + }, { + handle = CreateNamedPipeA( + fullServerName.toLocal8Bit().constData(), // pipe name + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access + PIPE_TYPE_MESSAGE | // message type pipe + PIPE_READMODE_MESSAGE | // message-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 (INVALID_HANDLE_VALUE == handle) { + return false; + } + pendingHandles.enqueue(handle); + return true; +} + +void QLocalServerThread::run() +{ + OVERLAPPED op; + HANDLE handleArray[2]; + memset(&op, 0, sizeof(op)); + handleArray[0] = op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + handleArray[1] = stopEvent; + HANDLE handle = INVALID_HANDLE_VALUE; + + forever { + if (INVALID_HANDLE_VALUE == handle) { + makeHandle(); + if (!pendingHandles.isEmpty()) + handle = pendingHandles.dequeue(); + } + if (INVALID_HANDLE_VALUE == handle) { + int windowsError = GetLastError(); + QString function = QLatin1String("QLocalServer::run"); + QString errorString = QLocalServer::tr("%1: Unknown error %2").arg(function).arg(windowsError); + emit error(QAbstractSocket::UnknownSocketError, errorString); + CloseHandle(handleArray[0]); + SetEvent(gotConnectionEvent); + return; + } + + BOOL isConnected = ConnectNamedPipe(handle, &op) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + if (!isConnected) { + switch (WaitForMultipleObjects(2, handleArray, FALSE, INFINITE)) + { + case WAIT_OBJECT_0 + 1: + CloseHandle(handle); + CloseHandle(handleArray[0]); + return; + } + } + emit connected(handle); + handle = INVALID_HANDLE_VALUE; + ResetEvent(handleArray[0]); + SetEvent(gotConnectionEvent); + } +} + +void QLocalServerPrivate::init() +{ + Q_Q(QLocalServer); + qRegisterMetaType<HANDLE>("HANDLE"); + q->connect(&waitForConnection, SIGNAL(connected(HANDLE)), + q, SLOT(_q_openSocket(HANDLE)), Qt::QueuedConnection); + q->connect(&waitForConnection, SIGNAL(finished()), + q, SLOT(_q_stoppedListening()), Qt::QueuedConnection); + q->connect(&waitForConnection, SIGNAL(terminated()), + q, SLOT(_q_stoppedListening()), Qt::QueuedConnection); + q->connect(&waitForConnection, SIGNAL(error(QAbstractSocket::SocketError, const QString &)), + q, SLOT(_q_setError(QAbstractSocket::SocketError, const QString &))); +} + +bool QLocalServerPrivate::removeServer(const QString &name) +{ + Q_UNUSED(name); + return true; +} + +bool QLocalServerPrivate::listen(const QString &name) +{ + fullServerName = waitForConnection.setName(name); + serverName = name; + waitForConnection.start(); + return true; +} + +void QLocalServerPrivate::_q_setError(QAbstractSocket::SocketError e, const QString &eString) +{ + error = e; + errorString = eString; +} + +void QLocalServerPrivate::_q_stoppedListening() +{ + Q_Q(QLocalServer); + if (!inWaitingFunction) + q->close(); +} + +void QLocalServerPrivate::_q_openSocket(HANDLE handle) +{ + Q_Q(QLocalServer); + q->incomingConnection((int)handle); +} + +void QLocalServerPrivate::closeServer() +{ + waitForConnection.stop(); + waitForConnection.closeServer(); +} + +void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut) +{ + Q_Q(QLocalServer); + if (!pendingConnections.isEmpty() || !q->isListening()) + return; + + DWORD result = WaitForSingleObject(waitForConnection.gotConnectionEvent, + (msecs == -1) ? INFINITE : msecs); + if (result == WAIT_TIMEOUT) { + if (timedOut) + *timedOut = true; + } else { + ResetEvent(waitForConnection.gotConnectionEvent); + QCoreApplication::instance()->processEvents(); + } +} + +QT_END_NAMESPACE diff --git a/src/network/socket/qlocalsocket.cpp b/src/network/socket/qlocalsocket.cpp new file mode 100644 index 0000000..327bfc6 --- /dev/null +++ b/src/network/socket/qlocalsocket.cpp @@ -0,0 +1,504 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 Window 9x. + + \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() +*/ + +/*! + \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 msec) + + Waits until the socket is connected, up to \a msec 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 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 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. + + \sa error(), errorString() +*/ + +/*! + \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. + + \sa state() +*/ + +/*! + 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(); +#ifndef Q_OS_WIN + 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: This 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 0000000..b8ad5c7 --- /dev/null +++ b/src/network/socket/qlocalsocket.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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()) +#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 0000000..dd48d0a --- /dev/null +++ b/src/network/socket/qlocalsocket_p.h @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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" +#else +# include "private/qnativesocketengine_p.h" +# include <qtcpsocket.h> +# include <qsocketnotifier.h> +# include <errno.h> +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) +static inline int qSocket(int af, int socketype, int proto) +{ + int ret; + while((ret = qt_socket_socket(af, socketype, proto)) == -1 && errno == EINTR){} + return ret; +} + +static inline int qBind(int fd, const sockaddr *sa, int len) +{ + int ret; + while((ret = QT_SOCKET_BIND(fd, (sockaddr*)sa, len)) == -1 && errno == EINTR){} + return ret; +} + +static inline int qConnect(int fd, const sockaddr *sa, int len) +{ + int ret; + while((ret = QT_SOCKET_CONNECT(fd, (sockaddr*)sa, len)) == -1 && errno == EINTR){} + return ret; +} + +static inline int qListen(int fd, int backlog) +{ + int ret; + while((ret = qt_socket_listen(fd, backlog)) == -1 && errno == EINTR){} + return ret; +} + +static inline int qAccept(int fd, struct sockaddr *addr, QT_SOCKLEN_T *addrlen) +{ + int ret; + while((ret = qt_socket_accept(fd, addr, addrlen)) == -1 && errno == EINTR){} + return ret; +} +#endif //#if !defined(Q_OS_WIN) && !defined(QT_LOCALSOCKET_TCP) + +#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() { + CloseHandle(overlapped.hEvent); + } + + void setErrorString(const QString &function); + void _q_notified(); + void _q_canWrite(); + void _q_pipeClosed(); + qint64 readData(char *data, qint64 maxSize); + qint64 bytesAvailable(); + bool readFromSocket(); + HANDLE handle; + OVERLAPPED overlapped; + QWindowsPipeWriter *pipeWriter; + qint64 readBufferMaxSize; + QRingBuffer readBuffer; + QTimer dataNotifier; + QLocalSocket::LocalSocketError error; + bool readyReadEmitted; + bool pipeClosed; +#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(); + 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 0000000..a4c6aa9 --- /dev/null +++ b/src/network/socket/qlocalsocket_tcp.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 = QString(); + fullServerName = QString(); + 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 = QString(); + 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 = QString(); + d->fullServerName = QString(); + 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 0000000..a375e9b --- /dev/null +++ b/src/network/socket/qlocalsocket_unix.cpp @@ -0,0 +1,566 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocalsocket.h" +#include "qlocalsocket_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 <qdatetime.h> +#include <qdir.h> +#include <qdebug.h> + +#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 = QString(); + fullServerName = QString(); + 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 = QString(); + 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 = qSocket(PF_UNIX, SOCK_STREAM, 0))) { + d->errorOccurred(UnsupportedSocketOperationError, + QLatin1String("QLocalSocket::connectToServer")); + return; + } + + // 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; + } + + // _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 == qConnect(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->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! + if (delayConnect) { + delayConnect->setEnabled(false); + delete delayConnect; + delayConnect = 0; + } + 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 = QString(); + 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(); +} + +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(); + if (d->delayConnect) { + d->delayConnect->setEnabled(false); + delete d->delayConnect; + d->delayConnect = 0; + d->connectTimer->stop(); + delete d->connectTimer; + d->connectTimer = 0; + } + if (d->connectingSocket != -1) + ::close(d->connectingSocket); + d->connectingSocket = -1; + d->connectingName = QString(); + d->connectingOpenMode = 0; + d->serverName = QString(); + d->fullServerName = QString(); + 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 readfds; + FD_ZERO(&readfds); + FD_SET(d->connectingSocket, &readfds); + + 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. + QTime timer; + timer.start(); + while (state() == ConnectingState + && (-1 == msec || timer.elapsed() < msec)) { + result = ::select(d->connectingSocket + 1, &readfds, 0, 0, &timeout); + 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 0000000..e759d0b --- /dev/null +++ b/src/network/socket/qlocalsocket_win.cpp @@ -0,0 +1,537 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocalsocket.h" +#include "qlocalsocket_p.h" + +#include <private/qthread_p.h> +#include <qcoreapplication.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +#define NOTIFYTIMEOUT 100 + +void QLocalSocketPrivate::init() +{ + Q_Q(QLocalSocket); + QObject::connect(&dataNotifier, SIGNAL(timeout()), q, SLOT(_q_notified())); + overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); +} + +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; + 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), + error(QLocalSocket::UnknownSocketError), + readyReadEmitted(false), + pipeClosed(false), + state(QLocalSocket::UnconnectedState) +{ +} + +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; + QT_WA({ + localSocket = CreateFileW( + (TCHAR*)d->fullServerName.utf16(), // pipe name + permissions, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file + }, { + localSocket = CreateFileA( + d->fullServerName.toLocal8Bit().constData(), // pipe name + permissions, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + 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. + QT_WA({ + if (!WaitNamedPipeW((TCHAR*)d->fullServerName.utf16(), 5000)) + break; + }, { + if (!WaitNamedPipeA(d->fullServerName.toLocal8Bit().constData(), 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), openMode) { + d->handle = localSocket; + emit connected(); + } +} + +// This is reading from the buffer +qint64 QLocalSocket::readData(char *data, qint64 maxSize) +{ + Q_D(QLocalSocket); + if (d->readBuffer.isEmpty()) { + if (!d->readFromSocket()) { + if (d->pipeClosed) + return -1; + return 0; + } + } + + if (!d->dataNotifier.isActive() && d->threadData->eventDispatcher) + d->dataNotifier.start(NOTIFYTIMEOUT); + + if (d->readBuffer.isEmpty()) + return qint64(0); + + // If readFromSocket() read data, copy it to its destination. + if (maxSize == 1) { + *data = d->readBuffer.getChar(); + return 1; + } + + 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); + } + return readSoFar; +} + +/*! + \internal + read from the socket + */ +qint64 QLocalSocketPrivate::readData(char *data, qint64 maxSize) +{ + DWORD bytesRead = 0; + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + bool success = ReadFile(handle, data, maxSize, &bytesRead, &overlapped); + if (!success && GetLastError() == ERROR_IO_PENDING) + if (GetOverlappedResult(handle, &overlapped, &bytesRead, TRUE)) + success = true; + if (!success) { + setErrorString(QLatin1String("QLocalSocket::readData")); + return 0; + } + return bytesRead; +} + +/*! + \internal + Reads data from the socket into the readbuffer + */ +bool QLocalSocketPrivate::readFromSocket() +{ + qint64 bytesToRead = bytesAvailable(); + if (bytesToRead == 0) + return false; + + if (readBufferMaxSize && bytesToRead + > (readBufferMaxSize - readBuffer.size())) + bytesToRead = readBufferMaxSize - readBuffer.size(); + + char *ptr = readBuffer.reserve(bytesToRead); + qint64 readBytes = readData(ptr, bytesToRead); + if (readBytes == 0) { + readBuffer.chop(bytesToRead); + return false; + } + readyReadEmitted = false; + readBuffer.chop(int(bytesToRead - (readBytes < 0 ? qint64(0) : readBytes))); + return true; +} + +qint64 QLocalSocket::writeData(const char *data, qint64 maxSize) +{ + Q_D(QLocalSocket); + if (!d->pipeWriter) { + d->pipeWriter = new QWindowsPipeWriter(d->handle, this); + d->pipeWriter->start(); + connect(d->pipeWriter, SIGNAL(canWrite()), this, SLOT(_q_canWrite())); + } + return d->pipeWriter->write(data, maxSize); +} + +void QLocalSocket::abort() +{ + close(); +} + +/*! + The number of bytes available from the pipe + */ +qint64 QLocalSocketPrivate::bytesAvailable() +{ + Q_Q(QLocalSocket); + if (q->state() != QLocalSocket::ConnectedState) + return 0; + DWORD bytes; + if (PeekNamedPipe(handle, NULL, 0, NULL, &bytes, NULL)) { + return bytes; + } else { + if (ERROR_BROKEN_PIPE == GetLastError() && !pipeClosed) { + pipeClosed = true; + 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->readBuffer.size(); + 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 (d->readBuffer.indexOf('\n') != -1 || QIODevice::canReadLine()); +} + +void QLocalSocket::close() +{ + Q_D(QLocalSocket); + if (state() == UnconnectedState) + return; + + QIODevice::close(); + d->state = ClosingState; + emit stateChanged(d->state); + d->readyReadEmitted = false; + emit readChannelFinished(); + d->serverName = QString(); + d->fullServerName = QString(); + + if (state() != UnconnectedState && bytesToWrite() > 0) { + disconnectFromServer(); + return; + } + d->pipeClosed = false; + DisconnectNamedPipe(d->handle); + CloseHandle(d->handle); + d->handle = INVALID_HANDLE_VALUE; + d->state = UnconnectedState; + emit stateChanged(d->state); + emit disconnected(); + if (d->pipeWriter) { + delete d->pipeWriter; + d->pipeWriter = 0; + } + d->dataNotifier.stop(); +} + +bool QLocalSocket::flush() +{ + Q_D(QLocalSocket); + if (d->pipeWriter) + return d->pipeWriter->waitForWrite(0); + return false; +} + +void QLocalSocket::disconnectFromServer() +{ + Q_D(QLocalSocket); + 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(); + QIODevice::open(openMode); + d->handle = (int*)socketDescriptor; + d->state = socketState; + emit stateChanged(d->state); + if (d->threadData->eventDispatcher) + d->dataNotifier.start(NOTIFYTIMEOUT); + return true; +} + +void QLocalSocketPrivate::_q_canWrite() +{ + Q_Q(QLocalSocket); + if (state == QLocalSocket::ClosingState) + q->close(); +} + +void QLocalSocketPrivate::_q_notified() +{ + Q_Q(QLocalSocket); + if (0 != bytesAvailable()) { + if (readBufferMaxSize == 0 || readBuffer.size() < readBufferMaxSize) { + if (!readFromSocket()) { + return; + } + // wait until buffer is cleared before starting again + if (readBufferMaxSize && readBuffer.size() == readBufferMaxSize) { + dataNotifier.stop(); + } + } + if (!readyReadEmitted) { + readyReadEmitted = true; + q->emit 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; + QIncrementalSleepTimer timer(msecs); + forever { + d->_q_notified(); + 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); + QIncrementalSleepTimer timer(msecs); + forever { + d->_q_notified(); + if (bytesAvailable() > 0) { + if (!d->readyReadEmitted) { + d->readyReadEmitted = true; + emit readyRead(); + } + return true; + } + + Sleep(timer.nextSleepTime()); + if (timer.hasTimedOut()) + break; + } + + return false; +} + +bool QLocalSocket::waitForBytesWritten(int msecs) +{ + Q_D(const QLocalSocket); + if (!d->pipeWriter) + return false; + + QIncrementalSleepTimer timer(msecs); + forever { + if (d->pipeWriter->hadWritten()) + return true; + + if (d->pipeWriter->bytesToWrite() == 0) + 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. + if (d->pipeWriter->waitForWrite(0)) + return true; + + Sleep(timer.nextSleepTime()); + if (timer.hasTimedOut()) + break; + } + + return false; +} + +QT_END_NAMESPACE diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp new file mode 100644 index 0000000..b25b887 --- /dev/null +++ b/src/network/socket/qnativesocketengine.cpp @@ -0,0 +1,1140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QNATIVESOCKETENGINE_DEBUG + +/*! \class QNativeSocketEngine + \internal + + \brief The QNativeSocketEngine class provides low level access to a socket. + + \reentrant + \ingroup io + \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 "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. + return; + } + if (error != QAbstractSocket::SocketError(11)) + hasSetSocketError = true; + + socketError = error; + + switch (errorString) { + case NonBlockingInitFailedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to initialize non-blocking socket")); + break; + case BroadcastingInitFailedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to initialize broadcast socket")); + break; + case NoIpV6ErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Attempt to use IPv6 socket on a platform with no IPv6 support")); + break; + case RemoteHostClosedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The remote host closed the connection")); + break; + case TimeOutErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network operation timed out")); + break; + case ResourceErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Out of resources")); + break; + case OperationUnsupportedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unsupported socket operation")); + break; + case ProtocolUnsupportedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Protocol type not supported")); + break; + case InvalidSocketErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Invalid socket descriptor")); + break; + case HostUnreachableErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Host unreachable")); + break; + case NetworkUnreachableErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network unreachable")); + break; + case AccessErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Permission denied")); + break; + case ConnectionTimeOutErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Connection timed out")); + break; + case ConnectionRefusedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Connection refused")); + break; + case AddressInuseErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The bound address is already in use")); + break; + case AddressNotAvailableErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The address is not available")); + break; + case AddressProtectedErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The address is protected")); + break; + case DatagramTooLargeErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Datagram was too large to send")); + break; + case SendDatagramErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to send a message")); + break; + case ReceiveDatagramErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to receive a message")); + break; + case WriteErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Unable to write")); + break; + case ReadErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Network error")); + break; + case PortInuseErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Another socket is already listening on the same port")); + break; + case NotSocketErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "Operation on non-socket")); + break; + case InvalidProxyTypeString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "The proxy type is invalid for this operation")); + break; + case UnknownSocketErrorString: + socketErrorString = QLatin1String(QT_TRANSLATE_NOOP("QNativeSocketEngine", "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"); + } + + // Set the send and receive buffer sizes to a magic size, found + // most optimal for our platforms. + 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(); +} + +/*! + 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); +} + +/*! + 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; + } + 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); + 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(const 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; + } +#endif + + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + QNativeSocketEnginePrivate::TimeOutErrorString); + 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(const 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; + } +#endif + if (ret == 0) { + if (timedOut) + *timedOut = true; + d->setError(QAbstractSocket::SocketTimeoutError, + QNativeSocketEnginePrivate::TimeOutErrorString); + 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) { + 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 0000000..3366f2d --- /dev/null +++ b/src/network/socket/qnativesocketengine_p.h @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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" +#endif + +QT_BEGIN_NAMESPACE + +#ifndef Q_OS_WIN +// Almost always the same. If not, specify in qplatformdefs.h. +#if !defined(QT_SOCKOPTLEN_T) +# define QT_SOCKOPTLEN_T QT_SOCKLEN_T +#endif + +// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED +static inline int qt_socket_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *addrlen) +{ return ::accept(s, addr, static_cast<QT_SOCKLEN_T *>(addrlen)); } +#if defined(accept) +# undef accept +#endif + +// UnixWare 7 redefines listen -> _listen +static inline int qt_socket_listen(int s, int backlog) +{ return ::listen(s, backlog); } +#if defined(listen) +# undef listen +#endif + +// UnixWare 7 redefines socket -> _socket +static inline int qt_socket_socket(int domain, int type, int protocol) +{ return ::socket(domain, type, protocol); } +#if defined(socket) +# undef socket +#endif + +#endif + +class QNativeSocketEnginePrivate; + +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(); + + 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 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(); + 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 0000000..534f7ec --- /dev/null +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -0,0 +1,949 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QNATIVESOCKETENGINE_DEBUG + +#include "qnativesocketengine_p.h" +#include "qiodevice.h" +#include "qhostaddress.h" +#include "qvarlengtharray.h" +#include "qdatetime.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 + +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() +{ + // 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); + } +} + +/* + 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(struct sockaddr *sa, quint16 *port, QHostAddress *addr) +{ +#if !defined(QT_NO_IPV6) + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + Q_IPV6ADDR tmp; + memcpy(&tmp, &sa6->sin6_addr.s6_addr, sizeof(tmp)); + if (addr) { + QHostAddress tmpAddress; + tmpAddress.setAddress(tmp); + *addr = tmpAddress; +#ifndef QT_NO_IPV6IFNAME + char scopeid[IFNAMSIZ]; + if (::if_indextoname(sa6->sin6_scope_id, scopeid) > 0) { + addr->setScopeId(QLatin1String(scopeid)); + } else +#endif + addr->setScopeId(QString::number(sa6->sin6_scope_id)); + } + if (port) + *port = ntohs(sa6->sin6_port); + return; + } +#endif + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + if (port) + *port = ntohs(sa4->sin_port); + if (addr) { + QHostAddress tmpAddress; + tmpAddress.setAddress(ntohl(sa4->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_socket_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; + } + + // Ensure that the socket is closed on exec*(). + ::fcntl(socket, F_SETFD, FD_CLOEXEC); + 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; + 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; + } + + int v = -1; + QT_SOCKOPTLEN_T len = sizeof(v); + if (getsockopt(socketDescriptor, SOL_SOCKET, 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; + 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. + 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; + } + + return true; + } + case QNativeSocketEngine::AddressReusable: +#ifdef SO_REUSEPORT + n = SO_REUSEPORT; +#else + n = SO_REUSEADDR; +#endif + break; + case QNativeSocketEngine::BindExclusively: + return true; + case QNativeSocketEngine::ReceiveOutOfBandData: + n = SO_OOBINLINE; + break; + } + + return ::setsockopt(socketDescriptor, SOL_SOCKET, n, (char *) &v, sizeof(v)) == 0; +} + +bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, 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 (addr.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(addr.scopeId().toLatin1().data()); +#else + sockAddrIPv6.sin6_scope_id = addr.scopeId().toInt(); +#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_SOCKET_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_socket_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_socket_accept(socketDescriptor, 0, 0); +#if defined (QNATIVESOCKETENGINE_DEBUG) + qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor); +#endif + // Ensure that the socket is closed on exec*() + ::fcntl(acceptedDescriptor, F_SETFD, FD_CLOEXEC); + return acceptedDescriptor; +} + +qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const +{ + /* + Apparently, there is not consistency among different operating + systems on how to use FIONREAD. + + FreeBSD, Linux and Solaris all expect the 3rd argument to + ioctl() to be an int, which is normally 32-bit even on 64-bit + machines. + + IRIX, on the other hand, expects a size_t, which is 64-bit on + 64-bit machines. + + So, the solution is to use size_t initialized to zero to make + sure all bits are set to zero, preventing underflow with the + FreeBSD/Linux/Solaris ioctls. + */ + size_t nbytes = 0; + // gives shorter than true amounts on Unix domain sockets. + qint64 available = 0; + if (::ioctl(socketDescriptor, FIONREAD, (char *) &nbytes) >= 0) + available = (qint64) *((int *) &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. +#if !defined(QT_NO_IPV6) + struct sockaddr_storage storage; + sockaddr_in6 *storagePtrIPv6 = reinterpret_cast<sockaddr_in6 *>(&storage); + storagePtrIPv6->sin6_port = 0; +#else + struct sockaddr storage; +#endif + sockaddr *storagePtr = reinterpret_cast<sockaddr *>(&storage); + storagePtr->sa_family = 0; + + sockaddr_in *storagePtrIPv4 = reinterpret_cast<sockaddr_in *>(&storage); + storagePtrIPv4->sin_port = 0; + QT_SOCKLEN_T storageSize = sizeof(storage); + + // 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, storagePtr, &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) +{ +#if !defined(QT_NO_IPV6) + struct sockaddr_storage aa; +#else + struct sockaddr_in aa; +#endif + 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, (struct sockaddr *)&aa, &sz); + } while (recvFromResult == -1 && errno == EINTR); + + if (recvFromResult == -1) { + setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString); + } else if (port || address) { + qt_socket_getPortAndAddress((struct sockaddr *) &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; + do { + sentBytes = ::sendto(socketDescriptor, data, len, + 0, sockAddrPtr, sockAddrSize); + } while (sentBytes == -1 && errno == EINTR); + + 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; + +#if !defined(QT_NO_IPV6) + struct sockaddr_storage sa; +#else + struct sockaddr_in sa; +#endif + struct sockaddr *sockAddrPtr = (struct sockaddr *) &sa; + QT_SOCKLEN_T sockAddrSize = sizeof(sa); + + // Determine local address + memset(&sa, 0, sizeof(sa)); + if (::getsockname(socketDescriptor, sockAddrPtr, &sockAddrSize) == 0) { + qt_socket_getPortAndAddress(sockAddrPtr, &localPort, &localAddress); + + // Determine protocol family + switch (sockAddrPtr->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, sockAddrPtr, &sockAddrSize)) + qt_socket_getPortAndAddress(sockAddrPtr, &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 + ::close(socketDescriptor); +} + +qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len) +{ + Q_Q(QNativeSocketEngine); + + // ignore the SIGPIPE signal + qt_ignore_sigpipe(); + + // loop while ::write() returns -1 and errno == EINTR, in case + // of an interrupting signal. + ssize_t writtenBytes; + do { + writtenBytes = ::write(socketDescriptor, data, len); + } while (writtenBytes < 0 && errno == EINTR); + + 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::unbufferedRead: Invalid socket"); + return -1; + } + + ssize_t r = 0; + do { + r = ::read(socketDescriptor, data, maxSize); + } while (r == -1 && errno == EINTR); + + 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: + setError(QAbstractSocket::NetworkError, ReadErrorString); + break; + case ECONNRESET: + 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; + + QTime timer; + timer.start(); + + int retval; + do { + if (selectForRead) + retval = select(socketDescriptor + 1, &fds, 0, 0, timeout < 0 ? 0 : &tv); + else + retval = select(socketDescriptor + 1, 0, &fds, 0, timeout < 0 ? 0 : &tv); + + if (retval != -1 || errno != EINTR) + break; + + if (timeout > 0) { + // recalculate the timeout + timeout -= timer.elapsed(); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + if (timeout < 0) { + // oops, timeout turned negative? + retval = -1; + break; + } + } + } while (true); + + 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; + + QTime timer; + timer.start(); + + int ret; + do { + ret = select(socketDescriptor + 1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); + if (ret != -1 || errno != EINTR) + break; + + if (timeout > 0) { + // recalculate the timeout + timeout -= timer.elapsed(); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + if (timeout < 0) { + // oops, timeout turned negative? + ret = -1; + break; + } + } + } while (true); + 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 0000000..d140be2 --- /dev/null +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -0,0 +1,1211 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <winsock2.h> + +#include "qnativesocketengine_p.h" + +#include <qabstracteventdispatcher.h> +#include <qsocketnotifier.h> +#include <qdebug.h> +#include <qdatetime.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 + +#if !defined (QT_NO_IPV6) + +// Use our own defines and structs which we know are correct +# define QT_SS_MAXSIZE 128 +# define QT_SS_ALIGNSIZE (sizeof(__int64)) +# 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]; + __int64 __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 { + 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 + +#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, struct sockaddr *sa, quint16 *port, QHostAddress *address) +{ +#if !defined (QT_NO_IPV6) + if (sa->sa_family == AF_INET6) { + qt_sockaddr_in6 *sa6 = (qt_sockaddr_in6 *)sa; + 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->sa_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + 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; + 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) + // 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; + 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; + } + + int v = -1; + QT_SOCKOPTLEN_T len = sizeof(v); + if (getsockopt(socketDescriptor, SOL_SOCKET, 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; + 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; + } + + if (::setsockopt(socketDescriptor, SOL_SOCKET, 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; + +#if !defined (QT_NO_IPV6) + struct qt_sockaddr_storage sa; +#else + struct sockaddr_in sa; +#endif + struct sockaddr *pSa = (struct sockaddr *) &sa; + + QT_SOCKLEN_T sz = sizeof(sa); + + memset(&sa, 0, sizeof(sa)); + if (::getsockname(socketDescriptor, pSa, &sz) == 0) { + qt_socket_getPortAndAddress(socketDescriptor, pSa, &localPort, &localAddress); + // Determine protocol family + switch (pSa->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, pSa, &sz) == 0) { + qt_socket_getPortAndAddress(socketDescriptor, pSa, &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; + QT_SOCKLEN_T sockAddrSize; + + 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; + } + } + // 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 &address, quint16 port) +{ + struct sockaddr_in sockAddrIPv4; + qt_sockaddr_in6 sockAddrIPv6; + struct sockaddr *sockAddrPtr; + QT_SOCKLEN_T sockAddrSize; + + 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()) { + // Becuase 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; +} + + +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. +#if !defined(QT_NO_IPV6) + qt_sockaddr_in6 storage; + qt_sockaddr_in6 *storagePtrIPv6 = reinterpret_cast<qt_sockaddr_in6 *>(&storage); + storagePtrIPv6->sin6_port = 0; +#else + struct sockaddr storage; +#endif + sockaddr *storagePtr = reinterpret_cast<sockaddr *>(&storage); + storagePtr->sa_family = 0; + + sockaddr_in *storagePtrIPv4 = reinterpret_cast<sockaddr_in *>(&storage); + storagePtrIPv4->sin_port = 0; + QT_SOCKLEN_T storageSize = sizeof(storage); + + + 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, storagePtr, &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, + storagePtr, &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(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; + +#if !defined(QT_NO_IPV6) + qt_sockaddr_storage aa; +#else + struct sockaddr_in aa; +#endif + 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, (struct sockaddr *) &aa, &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 (win 9x) 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, (struct sockaddr *) &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; + QT_SOCKLEN_T sockAddrSize; + + qt_socket_setPortAndAddress(socketDescriptor, &sockAddrIPv4, &sockAddrIPv6, port, address, &sockAddrPtr, &sockAddrSize); + + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based && len > qint64(qt_socket_getMaxMsgSize(socketDescriptor))) { + // WSAEMSGSIZE is not reliable enough (win 9x) so we check max size our self. + setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString); + } else { + 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; + // don't send more than 49152 per call to WSASendTo to avoid getting a WSAENOBUFS + for (;;) { + qint64 bytesToSend = qMin<qint64>(49152, len - ret); + 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); + + if (socketRet != SOCKET_ERROR) { + if (ret == len) + break; + else + continue; + } else if (WSAGetLastError() == WSAEWOULDBLOCK) { + break; + } else { + int err = WSAGetLastError(); + WS_ERROR_DEBUG(err); + switch (err) { + case WSAECONNRESET: + case WSAECONNABORTED: + ret = -1; + setError(QAbstractSocket::NetworkError, WriteErrorString); + q->close(); + break; + default: + break; + } + break; + } + } + +#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: + setError(QAbstractSocket::NetworkError, ReadErrorString); + 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] = 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 + ret = select(0, 0, &fds, 0, timeout < 0 ? 0 : &tv); + + 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; + + int ret = 0; + + memset(&fdread, 0, sizeof(fd_set)); + if (checkRead) { + fdread.fd_count = 1; + fdread.fd_array[0] = socketDescriptor; + } + memset(&fdwrite, 0, sizeof(fd_set)); + if (checkWrite) { + fdwrite.fd_count = 1; + fdwrite.fd_array[0] = socketDescriptor; + } + + 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, 0, timeout < 0 ? 0 : &tv); +#else + ret = select(1, &fdread, &fdwrite, 0, timeout < 0 ? 0 : &tv); +#endif + if (readEnabled) + readNotifier->setEnabled(true); + + if (ret <= 0) + return ret; + + *selectForRead = FD_ISSET(socketDescriptor, &fdread); + *selectForWrite = FD_ISSET(socketDescriptor, &fdwrite); + + return ret; +} + +void QNativeSocketEnginePrivate::nativeClose() +{ +#if defined (QTCPSOCKETENGINE_DEBUG) + qDebug("QNativeSocketEnginePrivate::nativeClose()"); +#endif + linger l = {1, 0}; + ::setsockopt(socketDescriptor, SOL_SOCKET, SO_DONTLINGER, (char*)&l, sizeof(l)); + ::closesocket(socketDescriptor); +} + +QT_END_NAMESPACE diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp new file mode 100644 index 0000000..c41e32d --- /dev/null +++ b/src/network/socket/qsocks5socketengine.cpp @@ -0,0 +1,1850 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "qdatetime.h" +#include "qmutex.h" +#include "qthread.h" +#include "qcoreapplication.h" +#include "qurl.h" +#include "qauthenticator.h" +#include <qendian.h> + +QT_BEGIN_NAMESPACE + +static const int MaxWriteBufferSize = 128*1024; + +//#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; + QDateTime 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 = QDateTime::currentDateTime(); + 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.secsTo(QDateTime::currentDateTime()) > 350) { + 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); + 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); + 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(); + } 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(); + } +} + +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; + QTime 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; + 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; + QTime 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 +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 + +int QSocks5SocketEngine::option(SocketOption option) const +{ + Q_UNUSED(option); + return -1; +} + +bool QSocks5SocketEngine::setOption(SocketOption option, int value) +{ + Q_UNUSED(option); + Q_UNUSED(value); + 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; + + QTime 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; + + QTime 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; + + QTime 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; + } + QSocks5SocketEngine *engine = new QSocks5SocketEngine(parent); + engine->setProxy(proxy); + return engine; +} + +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 0000000..e11b1b6 --- /dev/null +++ b/src/network/socket/qsocks5socketengine_p.h @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + 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 + + 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/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp new file mode 100644 index 0000000..c1fedc3 --- /dev/null +++ b/src/network/socket/qtcpserver.cpp @@ -0,0 +1,643 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QTCPSERVER_DEBUG + +/*! \class QTcpServer + + \brief The QTcpServer class provides a TCP-based server. + + \reentrant + \ingroup io + \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. + + \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 "qnativesocketengine_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; + } + 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(); + d->socketEngine->deleteLater(); + 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); + 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. + + \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(). + + \sa newConnection(), nextPendingConnection() +*/ +void QTcpServer::incomingConnection(int socketDescriptor) +{ +#if defined (QTCPSERVER_DEBUG) + qDebug("QTcpServer::incomingConnection(%i)", socketDescriptor); +#endif + + QTcpSocket *socket = new QTcpSocket(this); + socket->setSocketDescriptor(socketDescriptor); + 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 0000000..71fdde8 --- /dev/null +++ b/src/network/socket/qtcpserver.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + +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 0000000..086a420 --- /dev/null +++ b/src/network/socket/qtcpsocket.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QTCPSOCKET_DEBUG + +/*! + \class QTcpSocket + + \brief The QTcpSocket class provides a TCP socket. + + \reentrant + \ingroup io + \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. + + \sa QTcpServer, QUdpSocket, QFtp, QHttp, {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 0000000..ade70be --- /dev/null +++ b/src/network/socket/qtcpsocket.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTCPSOCKET_H +#define QTCPSOCKET_H + +#include <QtNetwork/qabstractsocket.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 0000000..3016cdb --- /dev/null +++ b/src/network/socket/qtcpsocket_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..820404d --- /dev/null +++ b/src/network/socket/qudpsocket.cpp @@ -0,0 +1,424 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//#define QUDPSOCKET_DEBUG + +/*! \class QUdpSocket + + \reentrant + \brief The QUdpSocket class provides a UDP socket. + + \ingroup io + \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. + + Example: + + \snippet doc/src/snippets/code/src_network_socket_qudpsocket.cpp 0 + + With QUdpSocket, you can also establish a virtual connection to a + UDP server using connectToHost() and then use read() and write() + to exchange datagrams without specifying the receiver for each + datagram. + + The \l{network/broadcastsender}{Broadcast Sender} and + \l{network/broadcastreceiver}{Broadcast Receiver} examples + illustrate how to use QUdpSocket in applications. + + \sa QTcpSocket +*/ + +/*! \enum QUdpSocket::BindFlag + \since 4.1 + + This enum describes the different flags you can pass to modify the + behavior of QUdpSocket::bind(). + + \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 "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() || socketEngine->protocol() != proto) { + 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); +} + +/*! + 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 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 0000000..4e02f84 --- /dev/null +++ b/src/network/socket/qudpsocket.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 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 + + 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 0000000..b1fe64a --- /dev/null +++ b/src/network/socket/socket.pri @@ -0,0 +1,46 @@ +# Qt network socket + +HEADERS += socket/qabstractsocketengine_p.h \ + socket/qnativesocketengine_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/qnativesocketengine.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 + +unix:SOURCES += socket/qnativesocketengine_unix.cpp \ + socket/qlocalsocket_unix.cpp \ + socket/qlocalserver_unix.cpp + + +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 +} + + diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp new file mode 100644 index 0000000..e2d0743 --- /dev/null +++ b/src/network/ssl/qssl.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 io + \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 - the default protocol. + \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. + + 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 0000000..bd0d943 --- /dev/null +++ b/src/network/ssl/qssl.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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, + AnyProtocol, + 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 0000000..a2ba644 --- /dev/null +++ b/src/network/ssl/qsslcertificate.cpp @@ -0,0 +1,795 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslCertificate + \brief The QSslCertificate class provides a convenient API for an X509 certificate. + \since 4.3 + + \reentrant + \ingroup io + \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 notValidBefore() and + notValidAfter() to check when the certificate was issued, 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 + +/*! + 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) +{ + d->ref.ref(); +} + +/*! + Destroys the QSslCertificate. +*/ +QSslCertificate::~QSslCertificate() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + Copies the contents of \a other into this certificate, making the two + certificates identical. +*/ +QSslCertificate &QSslCertificate::operator=(const QSslCertificate &other) +{ + qAtomicAssign(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 only checks that the current + data-time is within the date-time range during which the + certificate is considered valid. No other checks are + currently performed. + + \sa isNull() +*/ +bool QSslCertificate::isValid() const +{ + const QDateTime currentTime = QDateTime::currentDateTime(); + return currentTime >= d->notValidBefore && currentTime <= d->notValidAfter; +} + +/*! + Clears the contents of this certificate, making it a null + certificate. + + \sa isNull() +*/ +void QSslCertificate::clear() +{ + if (isNull()) + return; + if (d->ref == 1) + delete d; + else + d->ref.deref(); + + d = new QSslCertificatePrivate; +} + +/*! + Returns the certificate's version string. +*/ +QByteArray QSslCertificate::version() const +{ + return d->versionString; +} + +/*! + Returns the certificate's serial number string. +*/ +QByteArray QSslCertificate::serialNumber() const +{ + return d->serialNumberString; +} + +/*! + Returns a cryptographic digest of this certificate. By default, + and 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 +{ + 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 +{ + // ### Use a QByteArray for the keys in the map + 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 +{ + 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 +{ + // ### Use a QByteArray for the keys in the map + 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 *altNames = (STACK *)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 = QLatin1String(QByteArray(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_free(altNames); + } + + 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()) { + 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; + + certificate.d->issuerInfo = + _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_issuer_name(x509), 0, 0)); + certificate.d->subjectInfo = + _q_mapFromOnelineName(q_X509_NAME_oneline(q_X509_get_subject_name(x509), 0, 0)); + + 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 = pem.at(*offset); + + // ignore extra whitespace at the end of the line + while (ch == ' ' && *offset < pem.size()) + ch = pem.at(++*offset); + + if (ch == '\n') { + *offset++; + 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 (!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; +} + +#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 0000000..d4597cd --- /dev/null +++ b/src/network/ssl/qsslcertificate.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <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: + 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 0000000..cbf374b --- /dev/null +++ b/src/network/ssl/qsslcertificate_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(); + ref = 1; + } + + ~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); + + 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 0000000..505c662 --- /dev/null +++ b/src/network/ssl/qsslcipher.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslCipher + \brief The QSslCipher class represents an SSL cryptographic cipher. + \since 4.3 + + \reentrant + \ingroup io + \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> + +QT_BEGIN_NAMESPACE +#endif + +/*! + 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 = *other.d; +} + +/*! + Destroys the QSslCipher object. +*/ +QSslCipher::~QSslCipher() +{ + delete d; +} + +/*! + Copies the contents of \a other into this cipher, making the two + ciphers identical. +*/ +QSslCipher &QSslCipher::operator=(const QSslCipher &other) +{ + *d = *other.d; + 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 0000000..404dc3d --- /dev/null +++ b/src/network/ssl/qsslcipher.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLCIPHER_H +#define QSSLCIPHER_H + +#include <QtCore/qstring.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: + 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 0000000..46e5054 --- /dev/null +++ b/src/network/ssl/qsslcipher_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..4a0f226 --- /dev/null +++ b/src/network/ssl/qsslconfiguration.cpp @@ -0,0 +1,545 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslconfiguration.h" +#include "qsslconfiguration_p.h" +#include "qsslsocket.h" +#include "qmutex.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +template<> void QSharedDataPointer<QSslConfigurationPrivate>::detach() +{ + if (d && d->ref == 1) + return; + QSslConfigurationPrivate *x = (d ? new QSslConfigurationPrivate(*d) + : new QSslConfigurationPrivate); + x->ref.ref(); + if (d && !d->ref.deref()) + delete d; + d = x; +} + +/*! + \class QSslConfiguration + \brief The QSslConfiguration class holds the configuration and state of an SSL connection + \since 4.4 + + \reentrant + \inmodule QtNetwork + \ingroup io + \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(0) +{ +} + +/*! + 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 == 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 == 0; +} + +/*! + Returns the protocol setting for this SSL configuration. + + \sa setProtocol() +*/ +QSsl::SslProtocol QSslConfiguration::protocol() const +{ + return d ? d->protocol : QSsl::SslV3; +} + +/*! + 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 ? d->peerVerifyMode : QSslSocket::AutoVerifyPeer; +} + +/*! + 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 ? d->peerVerifyDepth : 0; +} + +/*! + 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 ? d->localCertificate : QSslCertificate(); +} + +/*! + 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 ? d->peerCertificate : QSslCertificate(); +} + +/*! + 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 ? d->peerCertificateChain : QList<QSslCertificate>(); +} + +/*! + 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 ? d->sessionCipher : QSslCipher(); +} + +/*! + 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 ? d->privateKey : QSslKey(); +} + +/*! + 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 ? d->ciphers : QList<QSslCipher>(); +} + +/*! + 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 moodified prior to the + handshake with addCaCertificate(), addCaCertificates(), and + setCaCertificates(). + + \sa setCaCertificates() +*/ +QList<QSslCertificate> QSslConfiguration::caCertificates() const +{ + return d ? d->caCertificates : QList<QSslCertificate>(); +} + +/*! + 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 SSLv3 + \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 0000000..fa3d646 --- /dev/null +++ b/src/network/ssl/qsslconfiguration.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, Trolltech 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; + + 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 0000000..3c4dbee --- /dev/null +++ b/src/network/ssl/qsslconfiguration_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** In addition, as a special exception, Trolltech 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::SslV3), + 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 0000000..daa6533 --- /dev/null +++ b/src/network/ssl/qsslerror.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslError + \brief The QSslError class provides an SSL error. + \since 4.3 + + \reentrant + \ingroup io + \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 + + \sa QSslError::errorString() +*/ + +#include "qsslerror.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. The two optional 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 = *other.d; +} + +/*! + Destroys the QSslError object. +*/ +QSslError::~QSslError() +{ + delete d; +} + +/*! + \since 4.4 + + Assigns the contents of \a other to this error. +*/ +QSslError &QSslError::operator=(const QSslError &other) +{ + *d = *other.d; + 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 = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "No error")); + break; + case UnableToGetIssuerCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The issuer certificate could not be found")); + break; + case UnableToDecryptCertificateSignature: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate signature could not be decrypted")); + break; + case UnableToDecodeIssuerPublicKey: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The public key in the certificate could not be read")); + break; + case CertificateSignatureFailed: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The signature of the certificate is invalid")); + break; + case CertificateNotYetValid: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate is not yet valid")); + break; + case CertificateExpired: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate has expired")); + break; + case InvalidNotBeforeField: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate's notBefore field contains an invalid time")); + break; + case InvalidNotAfterField: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate's notAfter field contains an invalid time")); + break; + case SelfSignedCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The certificate is self-signed, and untrusted")); + break; + case SelfSignedCertificateInChain: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root certificate of the certificate chain is self-signed, and untrusted")); + break; + case UnableToGetLocalIssuerCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The issuer certificate of a locally looked up certificate could not be found")); + break; + case UnableToVerifyFirstCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "No certificates could be verified")); + break; + case InvalidCaCertificate: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "One of the CA certificates is invalid")); + break; + case PathLengthExceeded: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The basicConstraints pathlength parameter has been exceeded")); + break; + case InvalidPurpose: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The supplied certificate is unsuited for this purpose")); + break; + case CertificateUntrusted: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root CA certificate is not trusted for this purpose")); + break; + case CertificateRejected: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The root CA certificate is marked to reject the specified purpose")); + break; + case SubjectIssuerMismatch: // hostname mismatch + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, + "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 = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "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 = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "The peer did not present any certificate")); + break; + case HostNameMismatch: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, + "The host name did not match any of the valid hosts" + " for this certificate")); + break; + case NoSslSupport: + break; + default: + errStr = QObject::tr(QT_TRANSLATE_NOOP(QSslError, "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 0000000..c120215 --- /dev/null +++ b/src/network/ssl/qsslerror.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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, + UnspecifiedError = -1 + }; + + QSslError(SslError error = NoError, const QSslCertificate &certificate = QSslCertificate()); + 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: + 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 0000000..8d550c0 --- /dev/null +++ b/src/network/ssl/qsslkey.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! + \class QSslKey + \brief The QSslKey class provides an interface for private and public keys. + \since 4.3 + + \reentrant + \ingroup io + \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 = passPhrase.isEmpty() + ? (void *)0 + : (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) +{ + d->ref.ref(); +} + +/*! + Destroys the QSslKey object. +*/ +QSslKey::~QSslKey() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + 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) +{ + qAtomicAssign(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() +{ + if (!d->ref.deref()) { + delete d; + 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 0000000..45d03f7 --- /dev/null +++ b/src/network/ssl/qsslkey.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSSLKEY_H +#define QSSLKEY_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qbytearray.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: + 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 0000000..c809015 --- /dev/null +++ b/src/network/ssl/qsslkey_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(); + ref = 1; + } + + 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; +}; + +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 0000000..4abf865 --- /dev/null +++ b/src/network/ssl/qsslsocket.cpp @@ -0,0 +1,2072 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 io + \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 SSLv3, 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 bytesWritten() 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 + + 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/}). + + \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 automaticaly 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/qdatetime.h> +#include <QtCore/qmutex.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 + + Variant of connectToHostEncrypted that enables to use a different hostname (\a sslPeerName) + for the certificate validation than 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; +} + +/*! + 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 descrypts 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::SslV3 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, QueryPeer for clients. + + \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, QueryPeer for clients. + + 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; +} + +/*! + \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 + QTcpSocket::close(); +} + +/*! + \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; + + // set the plain socket's buffer size to 1k if we have a limit + // see also the same logic in QSslSocketPrivate::createPlainSocket + if (d->plainSocket) { + if (d->mode == UnencryptedMode) + d->plainSocket->setReadBufferSize(size); + else + d->plainSocket->setReadBufferSize(size ? 1024 : 0); + } +} + +/*! + 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(); +} + +/*! + 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 (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; +} + +/*! + 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(). + + \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. If no system default database is + found, Qt will provide its own default database. You can override + the default CA certificate database with your own CA certificate + database using this function. + + 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, Qt will provide its own + default database. 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. + + \sa caCertificates() +*/ +QList<QSslCertificate> QSslSocket::defaultCaCertificates() +{ + return QSslSocketPrivate::defaultCaCertificates(); +} + +/*! + Returns the system default CA certificate database for your + system. This database is normally found in a standard place for + your system. If it is not found there, Qt will provide its own + default CA certificate database. 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() +{ + QSslSocketPrivate::ensureInitialized(); + 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; + + QTime 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; + + QTime 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); + + QTime 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); + + QTime 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::ensureInitialized(); +} + +/*! + 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::startClientEncryption: 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->ignoreSslErrors = true; +} + +/*! + \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 + 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), readyReadEmittedPointer(0), plainSocket(0) +{ + QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration); +} + +/*! + \internal +*/ +QSslSocketPrivate::~QSslSocketPrivate() +{ +} + +/*! + \internal +*/ +void QSslSocketPrivate::init() +{ + mode = QSslSocket::UnencryptedMode; + autoStartHandshake = false; + connectionEncrypted = false; + ignoreSslErrors = false; + + 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() +{ + 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; +} + +/*! + \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(); + + 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); + 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); +} + +/*! + \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(); +} + +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 0000000..e4c683a --- /dev/null +++ b/src/network/ssl/qsslsocket.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + + 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); + + // 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(); + +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()) + 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 0000000..827f461 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -0,0 +1,929 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qmutex.h> +#include <QtCore/qthread.h> +#include <QtCore/qurl.h> +#include <QtCore/qvarlengtharray.h> + +static void initNetworkResources() +{ + // Initialize resources + Q_INIT_RESOURCE(network); +} + +QT_BEGIN_NAMESPACE + +// Useful defines +#define SSL_ERRORSTR() QString::fromLocal8Bit(q_ERR_error_string(q_ERR_get_error(), NULL)) + +/* \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 (unsigned long)QThread::currentThreadId(); +} +} // extern "C" + +QSslSocketBackendPrivate::QSslSocketBackendPrivate() + : ssl(0), + ctx(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::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(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Enable all bug workarounds. + 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(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Add all our CAs to this store. + foreach (const QSslCertificate &caCertificate, q->caCertificates()) + q_X509_STORE_add_cert(ctx->cert_store, (X509 *)caCertificate.handle()); + + // 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(SSL_ERRORSTR())); + 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(SSL_ERRORSTR())); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // Load private key + EVP_PKEY *pkey = q_EVP_PKEY_new(); + if (configuration.privateKey.algorithm() == QSsl::Rsa) + q_EVP_PKEY_assign_RSA(pkey, (RSA *)configuration.privateKey.handle()); + else + q_EVP_PKEY_assign_DSA(pkey, (DSA *)configuration.privateKey.handle()); + if (!q_SSL_CTX_use_PrivateKey(ctx, pkey)) { + q->setErrorString(QSslSocket::tr("Error loading private key, %1").arg(SSL_ERRORSTR())); + emit q->error(QAbstractSocket::UnknownSocketError); + 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 certificate public key, %1").arg(SSL_ERRORSTR())); + 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(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::UnknownSocketError); + emit q->error(QAbstractSocket::UnknownSocketError); + return false; + } + + // 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(SSL_ERRORSTR())); + 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 + + Declared static in QSslSocketPrivate, makes sure the SSL libraries have + been initialized. +*/ +bool QSslSocketPrivate::ensureInitialized() +{ + if (!q_resolveOpenSslSymbols()) + return false; + + // Check if the library itself needs to be initialized. + QMutexLocker locker(openssl_locks()->initLock()); + static int q_initialized = false; + if (!q_initialized) { + q_initialized = true; + + // Initialize resources + initNetworkResources(); + + // 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; + } + + resetDefaultCiphers(); + setDefaultCaCertificates(systemCaCertificates()); + } + return true; +} + +/*! + \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); +} + +QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates() +{ +#ifdef QQ_OS_UNIX + // Check known locations for the system's default bundle. ### On Windows, + // we should use CAPI to find the bundle, and not rely on default unix + // locations. + const char *standardLocations[] = {"/etc/ssl/certs/", +#if 0 + // KDE uses KConfig for its SSL store, + // but it also stores the bundle at + // this location + "$HOME/.kde/share/apps/kssl/ca-bundle.crt", +#endif + 0}; + const char **it = standardLocations; + QStringList nameFilter; + nameFilter << QLatin1String("*.pem") << QLatin1String("*.crt"); + while (*it) { + if (QDirIterator(QLatin1String(*it), nameFilter).hasNext()) + return certificatesFromPath(QLatin1String(*it)); + ++it; + } +#endif + + // Qt provides a default bundle when we cannot detect the system's default + // bundle. + QFile caBundle(QLatin1String(":/trolltech/network/ssl/qt-ca-bundle.crt")); + if (caBundle.open(QIODevice::ReadOnly | QIODevice::Text)) + return QSslCertificate::fromDevice(&caBundle); + + // Unreachable; return no bundle. + return QList<QSslCertificate>(); +} + +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(). + testConnection(); + 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(). + testConnection(); + 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(SSL_ERRORSTR())); + 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 (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); + int decryptedBytesRead = plainSocket->read(data.data(), pendingBytes); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::transmit: read" << decryptedBytesRead << "encrypted bytes from the socket"; +#endif + // Write encrypted data from the buffer into the read BIO. + q_BIO_write(readBio, data.constData(), decryptedBytesRead); + 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 (testConnection()) { +#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 testConnection() 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; + default: + // ### Handle errors better. + q->setErrorString(QSslSocket::tr("Error while reading: %1").arg(SSL_ERRORSTR())); + 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::testConnection() +{ + 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> ¤tError = 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: + // ### Handle errors better + q->setErrorString(QSslSocket::tr("Error during SSL handshake: %1").arg(SSL_ERRORSTR())); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); +#ifdef QSSLSOCKET_DEBUG + qDebug() << "QSslSocketBackendPrivate::testConnection: 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; + 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); + + QRegExp regexp(commonName, Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.exactMatch(peerName)) { + bool matched = false; + foreach (QString altName, configuration.peerCertificate + .alternateSubjectNames().values(QSsl::DnsEntry)) { + regexp.setPattern(altName); + if (regexp.exactMatch(peerName)) { + 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); + if (doVerifyPeer && !ignoreSslErrors) { + 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; + } +} + +QSslCipher QSslSocketBackendPrivate::sessionCipher() const +{ + if (!ssl || !ctx) + return QSslCipher(); + SSL_CIPHER *sessionCipher = q_SSL_get_current_cipher(ssl); + 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; +} + +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 0000000..b3be42a --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <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> + +QT_BEGIN_NAMESPACE + +class QSslSocketBackendPrivate : public QSslSocketPrivate +{ + Q_DECLARE_PUBLIC(QSslSocket) +public: + QSslSocketBackendPrivate(); + virtual ~QSslSocketBackendPrivate(); + + // SSL context + bool initSslContext(); + SSL *ssl; + SSL_CTX *ctx; + 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 testConnection(); + 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); +}; + +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 0000000..e09e764 --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -0,0 +1,650 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qsslsocket_openssl_symbols_p.h" + +#include <QtCore/qlibrary.h> +#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(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) +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(void, sk_free, STACK *a, a, return, DUMMYARG) +DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) +DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) +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) +DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return 0, return) +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 +DEFINEFUNC(SSL_CIPHER *, SSL_get_current_cipher, SSL *a, a, return 0, return) +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) +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) +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) +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) + +#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); + +#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("/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 + +static QPair<QLibrary*, QLibrary*> loadOpenSsl() +{ + QPair<QLibrary*,QLibrary*> pair; + pair.first = 0; + pair.second = 0; + +# ifdef Q_OS_WIN + QLibrary *ssleay32 = new QLibrary(QLatin1String("ssleay32")); + if (!ssleay32->load()) { + // Cannot find ssleay32.dll + delete ssleay32; + return pair; + } + + QLibrary *libeay32 = new QLibrary(QLatin1String("libeay32")); + if (!libeay32->load()) { + delete ssleay32; + delete libeay32; + return pair; + } + + pair.first = ssleay32; + pair.second = libeay32; + 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 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 (libssl->load() && libcrypto->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 (libssl->load() && libcrypto->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 (libssl->load() && libcrypto->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 +} + +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; + + QPair<QLibrary *, QLibrary *> libs = loadOpenSsl(); + if (!libs.first || !libs.second) + // failed to load them + return false; + +#ifdef SSLEAY_MACROS + RESOLVEFUNC(ASN1_dup) +#endif + 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_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_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) + 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) + 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_SSL + 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) +{ + char lBuffer[24]; + char *pBuffer = lBuffer; + + size_t lTimeLength = aTime->length; + char *pString = (char *) aTime->data; + + if (aTime->type == V_ASN1_UTCTIME) { + if ((lTimeLength < 11) || (lTimeLength > 17)) + return QDateTime(); + + memcpy(pBuffer, pString, 10); + pBuffer += 10; + pString += 10; + } else { + if (lTimeLength < 13) + return QDateTime(); + + memcpy(pBuffer, pString, 12); + pBuffer += 12; + pString += 12; + } + + 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; +} + +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 0000000..c6ae91e --- /dev/null +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(); +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); +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); +void q_sk_free(STACK *a); +int q_sk_num(STACK *a); +char * q_sk_value(STACK *a, int b); +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); +SSL_CTX *q_SSL_CTX_new(SSL_METHOD *a); +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 +SSL_CIPHER *q_SSL_get_current_cipher(SSL *a); +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); +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); +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(); +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(); + +// 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 0000000..69d3cf3 --- /dev/null +++ b/src/network/ssl/qsslsocket_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 + +class QSslSocketPrivate : public QTcpSocketPrivate +{ + Q_DECLARE_PUBLIC(QSslSocket) +public: + QSslSocketPrivate(); + virtual ~QSslSocketPrivate(); + + void init(); + bool initialized; + + QSslSocket::SslMode mode; + bool autoStartHandshake; + bool connectionEncrypted; + bool ignoreSslErrors; + bool* readyReadEmittedPointer; + + QRingBuffer readBuffer; + QRingBuffer writeBuffer; + + 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; + + static bool 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); + + // The socket itself, including private slots. + QTcpSocket *plainSocket; + void createPlainSocket(QIODevice::OpenMode openMode); + 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(); + + // 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; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/network/ssl/qt-ca-bundle.crt b/src/network/ssl/qt-ca-bundle.crt new file mode 100644 index 0000000..7755ca0 --- /dev/null +++ b/src/network/ssl/qt-ca-bundle.crt @@ -0,0 +1,1984 @@ +## +## ca-bundle.crt -- Bundle of CA Certificates +## +## This is a bundle of X.509 certificates of public +## Certificate Authorities (CA). +## + +-----BEGIN CERTIFICATE----- +MIICfTCCAeagAwIBAgIEAgAAuDANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSkwJwYD +VQQDEyBCYWx0aW1vcmUgQ3liZXJUcnVzdCBNb2JpbGUgUm9vdDAeFw0wMDA1MTIx +ODIwMDBaFw0yMDA1MTIyMzU5MDBaMGExCzAJBgNVBAYTAklFMRIwEAYDVQQKEwlC +YWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxKTAnBgNVBAMTIEJhbHRpbW9y +ZSBDeWJlclRydXN0IE1vYmlsZSBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCjbbE4Vqz8tVYh3sCQXSZHgsZ9jx+ghY8vu9ThHB3yJB8osC+5pKVvoiIg +ZP6ERzx+K2xparjUwJaOjFINzW9B1L8ErqeBLy2YSNLBlKO1GV1dUWT0jkGwm8At +IqBexthaEmO8EUpeJhId4iYF5g9fIh96X3aUrs9aKA6rRdoiMQIDAQABo0IwQDAd +BgNVHQ4EFgQUyeKPwAImWrbAB+N/lAcY2y6lmnAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEFBQADgYEAUwgLJgl4QnPU7Hp3Rw3j +CzNx764zFE37+v0at1H15JkcBnHXKRnX5hUgUVFGbU/eGEmY0Ph4u3HojQEG1ddk +j5TfR/6ghWk2qS9CemhKEtaLC3BECqQE7yaIwTVxOF0bW0hC8OeUHHCVNKir9avi +eK318FL9m+pCDOjYVL5TZvU= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV +UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL +EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ +BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x +ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg +bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ +j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV +Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG +SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx +JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI +RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw +MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5 +fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i ++DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG +SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN +QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+ +gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGpMQsw +CQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENp +dHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UE +CxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0B +CQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDExODE4NTVaFw0wODExMjgx +ODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMO +U2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0 +IENvLjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx +ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdf +WvnTLnUv2Chi0ZMv/E3Uq4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uK +xBmd9LIO/BZsubEFkoPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBE +zUNKcI5YhZXhTizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F +5X5yP4WdlGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMv +OnNn7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+Legz +ZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvVWlHG4VME +lo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX8ngvYzZAOONG +Dx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn86Oawde3uPclwx12q +gUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsTF7ANUkz+/m9c4pFuHf2k +Ytdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV +UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL +EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ +BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x +ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/ +k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso +LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o +TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG +SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx +JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI +RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3 +MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C +TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5 +WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG +SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR +xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL +B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGpMQsw +CQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENp +dHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UE +CxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0B +CQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAyMjQ2MTZaFw0wODExMjcy +MjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMO +U2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0 +IENvLjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx +ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbV +p9oaBBg5kkp4o4HC9Xd6ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWw +BZoPFflrWXJW8vo5/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl +5WJp3OXuAFK9MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi +3sOP17ihYqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+ +QVCvbK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWogWxyQ +2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6HE3K1GjN +I3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV6YyDfFk/xPEL +553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8PzGn0EdzMzkbzE5q +10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30sPDst2yC7S8xmUJMqbIN +uBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50cnVzdC5u +ZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBsaW1pdHMgbGlh +Yi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBaMIHJMQswCQYDVQQGEwJVUzEU +MBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9D +bGllbnRfQ0FfSW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMq +RW50cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0G +CSqGSIb3DQEBAQUAA4GLADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo +6oT9n3V5z8GKUZSvx1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux +5zDeg7K6PvHViTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zm +AqTmT173iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSC +ARkwggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50 +cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5m +by9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMp +IDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQg +Q2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCyg +KqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9DbGllbnQxLmNybDArBgNV +HRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkxMDEyMTkyNDMwWjALBgNVHQ8E +BAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW/O5bs8qZdIuV6kwwHQYDVR0OBBYE +FMT7nCl7l81MlvzuW7PKmXSLlepMMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7 +pFuPeJoSSJn59DXeDDYHAmsQOokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzz +wy5E97BnRqqS5TvaHBkUODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/a +EkP/TOYGJqibGapEPHayXOw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 +MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE +ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j +b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg +U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA +A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ +I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 +wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC +AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb +oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 +BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p +dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk +MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp +b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 +MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi +E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa +MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI +hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN +95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd +2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIzCCAoygAwIBAgIENeHvHjANBgkqhkiG9w0BAQUFADBPMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEuMCwGA1UECxMlRXF1aWZheCBQcmVtaXVtIENl +cnRpZmljYXRlIEF1dGhvcml0eTAeFw05ODA4MjQyMjU0MjNaFw0xODA4MjQyMjU0 +MjNaME8xCzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdFcXVpZmF4MS4wLAYDVQQLEyVF +cXVpZmF4IFByZW1pdW0gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQDOoQaOBswIC8GGqN4g1Q0O0Q3En+pq2bPCMkdAb4qI +pAm9OCwd5svmpPM269rrvPxkswf2Lbyqzp8ZSGhK/PWiRX4JEPWPs0lcIwY56hOL +uAvNkR12X9k3oUT7X5DyZ7PNGJlDH3YSawLylYM4Q8L2YjTKyXhdX9LYupr/vhBg +WwIDAQABo4IBCjCCAQYwcQYDVR0fBGowaDBmoGSgYqRgMF4xCzAJBgNVBAYTAlVT +MRAwDgYDVQQKEwdFcXVpZmF4MS4wLAYDVQQLEyVFcXVpZmF4IFByZW1pdW0gQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIw +MTgwODI0MjI1NDIzWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUFe6yKFmrbuX4 +z4uB9CThrj91G5gwHQYDVR0OBBYEFBXusihZq27l+M+LgfQk4a4/dRuYMAwGA1Ud +EwQFMAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEB +BQUAA4GBAL0LnCepA9so3JipS9DRjqeoGlqR4Jzx9xh8LiKeNh/JqLXNRkpu+jUH +G4YI65/iqPmdQS06rlxctl80BOv8KmCw+3TkhellOJbuFcfGd2MSvYpoH6tsfdrK +XBPO6snrCVzFc+cSAdXZUwee4A+W8Iu0u0VIn4bFGVWgy5bFA/xI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT +ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw +MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj +dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l +c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC +UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc +58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ +o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr +aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA +A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA +Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv +8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc +MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT +ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw +MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j +LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ +KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo +RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu +WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw +Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK +eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM +zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ +WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN +/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2Vj +dXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0 +NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYD +VQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0G +vxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/ +BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0C +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEX +MBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJl +IGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkw +NjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBq +y/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy +0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1 +E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD +VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv +bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv +b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH +iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS +r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 +04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r +GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 +3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P +lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgICAbYwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVVMx +GDAWBgNVBAoTD0dURSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RFIEN5YmVyVHJ1 +c3QgU29sdXRpb25zLCBJbmMuMR4wHAYDVQQDExVHVEUgQ3liZXJUcnVzdCBSb290 +IDUwHhcNOTgwODE0MTQ1MDAwWhcNMTMwODE0MjM1OTAwWjBwMQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xHjAcBgNVBAMTFUdURSBDeWJlclRydXN0IFJv +b3QgNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALwSbj+KfHqXAewe +uzlaAvR4RKJIG457SVJ6uHtHs6+Um2+7lvoramVcuByUc76/iQoigO5X/IwFu3Cf +lzkE2qOHXKjlyq/AM5rVN1xLrOSA0KYjYPv9ci6UncfOwgQy73hgXe2thw9FZR48 +mgqavl0dmezn8tHGehfZrZtUln/EfGC/haoVNR1A2hG87FQhKC0joajwzy3N3fx+ +D17hZQdWywe00lboXjHMGGPEhtIthc+Tkqtt/mg5+95zvYb45EZ66p8My/QZ/mO8 +0Sx7iDM29uThnAxTgWAc2i6rlqkWiBNQmbK9Vd8VMH7o5Zj7cH5stQf8/Ea30O03 +ln4y/iECAwEAAaNaMFgwEgYDVR0TAQH/BAgwBgEB/wIBBTAOBgNVHQ8BAf8EBAMC +AQYwFwYDVR0gBBAwDjAMBgoqhkiG+GMBAgEDMBkGA1UdDgQSBBB2CkkhOEyf3vjE +ScdxcZGdMA0GCSqGSIb3DQEBBQUAA4IBAQBBOtQYW9q43iEc4Y4J5fFoNP/elvQH +9ac886xKsZv6kvqb7eYyIapKdsXcTzjl39WG5NXIdn2Y17HNj021kSNsi4rr6nzv +FJTExvAfSi0ycWMrY5EmAgm2gB3t4sy4f9uHY8jh0GwmsTUdQGYQG82VVBgzYewT +T9oT95mvPtDPjqZyorPDBZrJJ32SzH5SjbOrcG2eiZ9N6xp1wpiq1QIW1wyKvyXk +6y28mOlYOBl8uTf+2+KZCHMGx5eDan0QAS8yuRcFSmXmL86+XlOmgumaUwqEdC2D +ysiUFnZflGEo8IWnObvXi9moshMdVAk0JH0ggX1mfqKQdFwQxr3sqxvC +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDnjCCAoagAwIBAgILAgAAAAAA1ni50a8wDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05OTAxMjgxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMF8xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRQwEgYDVQQLEwtQYXJ0bmVycyBDQTEfMB0GA1UEAxMWR2xv +YmFsU2lnbiBQYXJ0bmVycyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANIs+DKsShJ6N8gpkaWujG4eDsA0M4jlM3EWHHiEaMMYNFAuFj6xlIJPsZqf +APjGETXGaXuYAq0ABohs50wzKACIJ0Yfh7NxdWO8MruI3mYYDlAGk7T2vBQ3MD0i +3z3/dX7ZChrFn7P80KyzCHqJ0wHoAFznSgs9TXsmordiBovaRt2TFz8/WwJLC7aI +IBGSAK27xy7U40Wu9YlafI2krYVkMsAnjMbyioCShiRWWY10aKKDQrOePVBBhm8g +bvb9ztMZ4zLMj+2aXm0fKPVSrG4YXvg90ZLlumwBiEsK8i3eZTMFQqBMqjF2vv2/ +gXj5cRxGXi0VlS0wWY5MQdFiqz0CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgAGMB0G +A1UdDgQWBBRDJI1wFQhiVZxPDEAXXYZeD6JM+zAfBgNVHSMEGDAWgBRge2YaRQ2X +yolQL30EzTSo//z9SzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IB +AQBm7bSIaRGZgiGDrKFti5uErQ8tyB6Mynt+rarUjt4H1p5Fx6W4nAc5YCVVGsBP +GeXPFylJiRg1ZuXrKEBOV8mvs+S4IAWjO5VQkUmUKX0s5YhBpUWIXp2CJ/fS71u1 +T5++/jVlLFVkn+FR2iJhd7pYTo/GeVlZbjCAok+QbiELrdBoOZAQm+0iZW8eETjm +f4zS8zltR9Uh6Op1OkHRrfYWnV0LIb3zH2MGJR3BHzVxLOsgGdXBsOw95W/tAgc/ +E3tmktZEwZj3X1CLelvCb22w0fjldKBAN6MlD+Q9ymQxk5BcMHu5OTGaXkzNuUFP +UOQ9OK7IZtnHO11RR6ybq/Kt +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgILAgAAAAAA1ni4N88wDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MTUxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDEgQ0ExJjAkBgNV +BAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAxIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAvSA1R9Eo1gijEjkjRw29cCFSDlcxlaY0V2vsfkN5 +wwZSSM28taGZvdgfMrzP125ybS53IpCCTkuPmgwBQprZcFm2nR/mY9EMrR1O+IWB ++a7vn6ZSYUR5GnVF4GFWRW1CjD1yy6akErea9dZg0GBQs46mpuy09BLNf6jO77Ph +hTD+csTm53eznlhB1lGDiAfGtmlPNt7RC0g/vdafIXRkbycGPkv9Dqabv6RIV4yQ +7okYCwKBGL5n/lNgiCe6o3M0S1pWtN5zBe2Yll3sSudA/EsJYuvQ4zFPhdF6q1ln +K/uID+uqg701/WEn7GYOQlf3acIM7/xqwm5J2o9BOK5IqQIDAQABo2MwYTAOBgNV +HQ8BAf8EBAMCAAYwHQYDVR0OBBYEFPzgZvZaNZnrQB7SuB5DvJiOH4rDMB8GA1Ud +IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBAJujCETO8pCdcfMyswVqterPKZjeVT6gFn0GekTWr9L6 +E1iM+BzHqx20G+9paJhcCDmP4Pf7SMwh57gz2wWqNCRsSuXpe2Deg7MfCr5BdfzM +MEi3wSYdBDOqtnjtKsu6VpcybvcxlS5G8hTuJ8f3Yom5XFrTOIpk9Te08bM0ctXV +IT1L13iT1zFmNR6j2EdJbxyt4YB/+JgkbHOsDsIadwKjJge3x2tdvILVKkgdY89Q +Mqb7HBhHFQpbDFw4JJoEmKgISF98NIdjqy2NTAB3lBt2uvUWGKMVry+U9ikAdsEV +F9PpN0121MtLKVkkrNpKoOpj3l9Usfrz0UXLxWS0cyE= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgILAgAAAAAA1ni4jY0wDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05OTAxMjgxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDIgQ0ExJjAkBgNV +BAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAyIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAkoz+7/RFjhdBbvzYvyFvqwadUsEsAJ0/joW4f0qP +vaBjKspJJ65agvR04lWS/8LRqnmitvrVnYIET8ayxl5jpzq62O7rim+ftrsoQcAi ++05IGgaS17/Xz7nZvThPOw1EblVB/vwJ29i/844h8egStfYTpdPGTJMisAL/7h0M +xKhrT3VoVujcKBJQ96gknS4kOfsJBd7lo2RJIdBofnEwkbFg4Dn0UPh6TZgAa3x5 +uk7OSuK6Nh23xTYVlZxkQupfxLr1QAW+4TpZvYSnGbjeTVNQzgfR0lHT7w2BbObn +bctdfD98zOxPgycl/3BQ9oNZdYQGZlgs3omNAKZJ+aVDdwIDAQABo2MwYTAOBgNV +HQ8BAf8EBAMCAAYwHQYDVR0OBBYEFHznsrEs3rGna+l2DOGj/U5sx7n2MB8GA1Ud +IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBAGPdWc6KeaqYnU7FiWQ3foqTZy8Q6m8nw413bfJcVpQZ +GmlgMEZdj/JtRTyONZd8L7hR4uiJvYjPJxwINFyIwWgk25GF5M/7+0ON6CUBG8QO +9wBCSIYfJAhYWoyN8mtHLGiRsWlC/Q2NySbmkoamZG6Sxc4+PH1x4yOkq8fVqKnf +gqc76IbVw08Y40TQ4NzzxWgu/qUvBYTIfkdCU2uHSv4y/14+cIy3qBXMF8L/RuzQ +7C20bhIoqflA6evUZpdTqWlVwKmqsi7N0Wn0vvi7fGnuVKbbnvtapj7+mu+UUUt1 +7tjU4ZrxAlYTiQ6nQouWi4UMG4W+Jq6rppm8IvFz30I= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgILAgAAAAAA1ni41sMwDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05OTAxMjgxMjAw +MDBaFw0wOTAxMjgxMjAwMDBaMG0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRswGQYDVQQLExJQcmltYXJ5IENsYXNzIDMgQ0ExJjAkBgNV +BAMTHUdsb2JhbFNpZ24gUHJpbWFyeSBDbGFzcyAzIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAkV5WZdbAwAScv0fEXHt6MQH5WJaZ4xyEL9xWj631 +WYHVQ2ZdWpOMdcqp5xHBURAUYMks1HuvxneGq3onrm+VuQvKtkb7fhr0DRRt0slO +sq7wVPZcQEw2SHToVIxlZhCnvSu3II0FSa14fdIkI1Dj8LR5mwE5/6870y3u4UmN +jS88akFFL5vjPeES5JF1ns+gPjySgW+KLhjc4PKMjP2H2Qf0QJTJTk9D32dWb70D +UHyZZ6S5PJFsAm6E1vxG98xvGD4X8O8LZBZX5qyG8UiqQ8HJJ3hzREXihX26/7Ph ++xsFpEs7mRIlAVAUaq9d6sgM7uTa7EuLXGgTldzDtTA61wIDAQABo2MwYTAOBgNV +HQ8BAf8EBAMCAAYwHQYDVR0OBBYEFMw2zBe0RZEv7c87MEh3+7UUmb7jMB8GA1Ud +IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQEEBQADggEBAFeyVMy9lRdkYIm2U5EMRZLDPahsw8yyGPV4QXTYfaMn +r3cNWT6UHWn6idMMvRoB9D/o4Hcagiha5mLXt+M2yQ6feuPC08xZiQzvFovwNnci +yqS2t8FCZwFAY8znOGSHWxSWZnstFO69SW3/d9DiTlvTgMJND8q4nYGXpzRux+Oc +SOW0qkX19mVMSPISwtKTjMIVJPMrUv/jCK64btYsEs85yxIq56l7X5g9o+HMpmOJ +XH0xdfnV1l3y0NQ9355xqA7c5CCXeOZ/U6QNUU+OOwOuow1aTcN55zVYcELJXqFe +tNkio0RTNaTQz3OAxc+fVph2+RRMd4eCydx+XTTVNnU= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU +YHtmGkUNl8qJUC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B +AQQFAAOCAQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq7 +5bCdPTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q +gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT2iHR +rH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlDNPYPhyk7 +ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBgHcl5JLL2bP2o +Zg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD +VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT +ZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBlcnNvbmFsIEJhc2lj +IENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNpY0B0aGF3dGUuY29tMB4X +DTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgcsxCzAJBgNVBAYTAlpBMRUw +EwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UE +ChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2Vy +dmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBD +QTEoMCYGCSqGSIb3DQEJARYZcGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53 +dXLdjUmbllegeNTKP1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdK +wPQIcOk8RHtQfmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7 +G1sY0b8jkyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQF +AAOBgQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7 +c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95B21P +9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD +VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT +ZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVt +YWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUu +Y29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgdExCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEa +MBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBG +cmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhh +d3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfY +DFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5E +rHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVq +uzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN +BgkqhkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP +MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgCneSa +/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr5PjRznei +gQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD +VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT +ZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBlcnNvbmFsIFByZW1p +dW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXByZW1pdW1AdGhhd3RlLmNv +bTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5NTlaMIHPMQswCQYDVQQGEwJa +QTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xGjAY +BgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5nMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9u +IFNlcnZpY2VzIERpdmlzaW9uMSMwIQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJl +bWl1bSBDQTEqMCgGCSqGSIb3DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUu +Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0Vs +Bd/eJxZRNkERbGw77f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWI +Et12TfIa/G8jHnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYD +ZicRFTuqW/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH +b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVxeTBh +KXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1KzGJ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm +MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx +MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3 +dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl +cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3 +DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91 +yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX +L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG +7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e +QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ +qdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIRIjCCCQoCAQAwDQYJKoZIhvcNAQEFBQAwVzEPMA0GA1UEChMGVGhhd3RlMSEw +HwYDVQQLExhUaGF3dGUgVW5pdmVyc2FsIENBIFJvb3QxITAfBgNVBAMTGFRoYXd0 +ZSBVbml2ZXJzYWwgQ0EgUm9vdDAeFw05OTEyMDUxMzU2MDVaFw0zNzA0MDMxMzU2 +MDVaMFcxDzANBgNVBAoTBlRoYXd0ZTEhMB8GA1UECxMYVGhhd3RlIFVuaXZlcnNh +bCBDQSBSb290MSEwHwYDVQQDExhUaGF3dGUgVW5pdmVyc2FsIENBIFJvb3Qwgggi +MA0GCSqGSIb3DQEBAQUAA4IIDwAwgggKAoIIAQDiiQVtw3+tpok6/7vHzZ03seHS +IR6bYSoV53tXT1U80Lv52T0+przstK1TmhYC6wty/Yryj0QFxevT5b22RDnm+0e/ +ap4KlRjiaOLWltYhrYj99Rf109pCpZDtKZWWdTrah6HU9dOH3gVipuNmdJLPpby7 +32j/cXVWQVk16zNaZlHy0qMKwYzOc1wRby2MlYyRsf3P5a1WlcyFkoOQVUHJwnft ++aN0QgpoCPPQ0WX9Zyw0/yR/53nIBzslV92kDJg9vuDMGWXb8lSir0LUneKuhCMl +CTMStWoedsSL2UkAbF66H/Ib2mfKJ6qjRCMbg4LO8qsz7VSk3MmrWWXROA7BPhtn +j9Z1AeBVIt12d+yO3fTPeSJtuVcD9ZkIpzw+NPvEF64jWM0k8yPKagIolAGBNLRs +a66LGsOj0gk8FlT1Nl8k459KoeJkxhbDpoF6JDZHjsFeDvv5FXgE1g5Z2Z1YZmLS +lCkyMsh4uWb2tVbhbMYUS5ZSWZECJGpVR9c/tiMaYHeXLuJAr54EV56tEcXJQ3Dv +SLRerBxpLi6C1VuLvoK+GRRe5w0ix1Eb/x6b8TCPcTEGszQnj196ZoJPii0Tq0LP +IVael45mNg+Wm+Ur9AKpKmqMLMTDuHAsLSkeP1B3Hm0qVORVCpE4ocW1ZqJ2Wu4P +v7Rn4ShuD+E2oYLRv9R34cRnMpN4yOdUU/4jeeZozCaQ9hBjXSpvkS2kczJRIfK7 +Fd+qJAhIBt6hnia/uoO/fKTIoIy90v+8hGknEyQYxEUYIyZeGBTKLoiHYqNT5iG3 +uIV7moW7FSZy+Ln3anQPST+SvqkFt5knv78JF0uZTK0REHzfdDH2jyZfqoiuOFfI +VS3T+9gbUZm+JRs6usB9G+3O0km5z/PFfYmQgdhpSCAQo/jvklEYMosRGMA/G4VW +zlfJ8oJkxt8CCS5KES+xJ203UvDwFmHxZ43fh3Kvh9rP+1CUbtSUheuKLOoh9ZZK +RNXgzmp0RE3QBdOHFe020KSLZlVwk+5HBsF+LqUYeWfzKIXxcPcOg6R+VJ5adjLL +ZRu4zfvIKAPSVJHRp8WFQwgXdqXmL2cI2KGigi0M+MGvY9RQd21rRkpBhdWQX3kt +xOzXEYdAiuFo4mT4VTL7b5Ms2nfZIcEX5TYsTn6Qf6yUKzJnvjhQdriuQbnXIcUJ +TGDIo1HENJtXN9/LyTNXi+v7dp8ZTcVqHypFrivtL42npQDLBPolYi50SBvKKoy6 +27Z+9rsCfKnD21h4ob/w/hoQVRHO6GlOlmXGFwPWB2iMVIKuHCJVP/H0CZcowEb3 +TgslHfcH1wkdOhhXODvoMwbnj3hGHlv1BrbsuKYN8boTS9YYIN1pM0ozFa64yJiK +JyyTvC377jO/ZuZNurabBlVgl0u8RM1+9KHYqi/AAighFmJ42whU8vz0NOPGjxxD +V86QGkvcLjsokYk/eto1HY4s7kns9DOtyVOojJ8EUz4kHFLJEvliV6O87izrQHwg +I3ArlflzF4rRwRxpprc4mmf3cB16WgxAz2IPhTzCAk5+tfbFKimEsx83KuGqckLE +7Wsaj5IcXb7R8lvyq6qp0vW4pEErK5FuEkjKmNg3jcjtADC1tgROfpzahOzA+nvl +HYikU0awlORcG6ElLA9IUneXCWzsWxgzgwLlgn7NhSEwEf0nT8/kHuw/pVds6Sow +GSqI5cNpOKtvOXF/hOFBw+HMKokgUi6DD2w5P0stFqwt8CSsAHP0m7MGPwW4FIUf +q55cPJ5inQ5tO4AJ/ALqopd0ysf541bhw8qlpprAkOAkElPSwovavu0CQ15n4YmY +ee7LqsrDG9znpUalfGsWh7ZaKNfbJzxepb22Ud0fQ887Jsg6jSVhwUn0PBvJROqv +HMIrlAEqDjDRW4srR+XD0QQDmw45LNYn1OZwWtl1zyrYyQAF5BOI7MM5+4dhMDZD +A8ienKIGwi/F/PCAY7FUBKBMqS7G9XZ62NDk1JQR5RW1eAbcuICPmakgMz0QhUxl +Cco+WF5gk5qqYl3AUQYcXWCgDZxLQ/anFiGkh6rywS7ukjC4nt/fEAGLhglw2Gyo +t1AeFpa092f9NTohkCoyxwB7TQcQCbkvc9gYfmeZBE8G/FDHhZudQJ2zljf6pdyy +ck7vTgks/ZH9Tfe7pqE+q3uiA0CmqVUn4vr5Gc6HdarxdTbz87iR+JHDi3UTjkxl +mhY5auU06HqWWX81sAD9W2n8Qyb69Shu/ofZfiT7tKCCblSi/66/YrT0cgHCy5hH +mOFMtReAgM6PpijuHkVq+9/xHfxaO9bq9GwdYklXO4qPhurwUwTOnBZo/7q5/IgP +R/cCRHJAuMo7LVOd3DxWjFl7aBosjXG7bADHGs5vQJKxoy8P2UTyo3Aunu4OrjLQ +Oz6LB+rmebNcKeJ9a6he+Vox6AiWoowDmEbxuH2QVCbtdmL+numabl7JScdcNFMp +VNns5EbhgDt12d/7edWH8bqe6xnOTFJz5luHriVPOXnMxrj5EHvs8JtxpAWg0ynT +Tn8f9C0oeMxVlXsekS/MVhhzi7LbvGkH5tDYT+2i/1iFo23gSlO3Z32NDFxbe3co +AjVEegTTKEPIazAXXTK4KTW6dto7FEp2GFik+JI8nk0zb0ZrCNkxSGjd9PskVjSy +z2lmvkjSimYizfJpzcJTE0UpQSLWXZgftqSyo8LuAi9RG9yDpOxwJajUCGEyb+Sh +gS58Y3L6KWW8cETPXQIDAQABMA0GCSqGSIb3DQEBBQUAA4IIAQBVmjRqIgZpCUUz +x66pXMcJTpuGvEGQ1JRS9s0jKZRLIs3ovf6dzVLyve2rh8mrq0YEtL2iPyIwR1DA +S4x2DwP1ktKxLcR6NZzJc4frpp/eD3ON03+Z2LqPb8Tzvhqui6KUNpDi5euNBfT8 +Zd+V8cSUTRdW1588j1A853e/lYYmZPtq/8ba6YyuQrtp5TPG2OkNxlUhScEMtKP5 +m0tc3oNPQQPOKnloOH3wVEkg9bYQ/wjcM2aWm/8G3gCe185WQ5pR/HDN9vBRo7fN +tFyFYs1xt8YrIyvdw25AQvo3/zcc9npXlIeFI9fUycdfwU0vyQ3XXOycJe6eMIKR +lnK4dR34CWhXl7ItS+4l7HokKe5y1JwT26vcAwrYShTJCFdEXaG1U4A08hSXz1Le +og6KEOkU79BgvmGh8SVd1RhzP5MQypbus0DS26NVz1dapQ5PdUff6veQmm31cC4d +FBw3ZARZULDccoZvnDc9XSivc1Xv0u4kdHQT79zbMUn7P2P10wg+M6XnnQreUyxR +jmfbm0FlQVC91KSWbIe8EuCUx9PA5MtzWACD4awnhdadU51cvQo+A0OcDJH1bXv4 +QHJ1qxF2kSvhxqofcGl2cBUJ/pPQ1i23FWqbZ1y0aZ8lpn2K+30iqXHyzk6MuCEt +3v5BcQ3/nexzprsHT4gOWEcufqnCx3jdunqeTuAwTmNvhdQgQen6/kNF5/uverLO +pAUdIppYht/kzkyp/tgWpW/72M5We/XWIO/kR81jJP+5vvFIo8EBcua9wK3tJg3K +NJ/8Ai0gTwUgriE9DMIgPD/wBITcz4n9uSWRjtBD5rMgq1wt1UCeoEvY9LLMffFY +Co6H7YisNpbkVqARivKa0LNXozS7Gas44XRrIsQxzgHVGzbjHjhMM5PfQONZV06s +bnseWj3FHVusyBCCNQIisvx16BCRjcR9eJNHnhydrGtiAliM1hwj1q94woCcpKok +VBS1FJjG+CsaJMtxMgrimw5pa91+jGTRLmPvDn+xPohMnVXlyW4XBLdB/72KQcsl +MW9Edz9HsfyBiAeOBUkgtxHZaQMqA525M4Sa399640Zzo9iijFMZiFVMdLj2RIQr +0RQtTjkukmj/afyFYhvrVU/vJYRiRZnW2E5vP1MIfR0GlYGAf09OdDaYteKHcJjc +1/XcUhXmxtZ5ljl/j5XPq4BTrRsLRUAO1Bi9LN6Kd3b98kRHxiHQ5HTw2BgFyHww +csff8bv8AjCp9EImWQ2TBYKhc+005ThdzVCQ/pT8E7y9/KiiiKdzxLKo0V2IxAKi +evEEyf6MdMnvHWRBn6welmdkrKsoQced98CYG24HwmR9WoNmVig2nOf7HHcOKKDE +92t5OQQghMdXk7wboOq860LlqBH+/KxlzP34KIj0pZrlc1HgqJsNA3dO5eCYs4ja +febGnnwUZsEuU0qSBzegfuk9CeQVfM/9uEGl755mncReBx2H+EGt6ucv0kFjGDf5 +FONN0OX3Q/0V4/k2cwYm3wFPqcNO3iBGd5i0eiQrO3UrTliNm12kxxagvDKIP6GD +8wDI+NhY6WNdTCu18HJB2Kt3N9ZydK62NpzIpoNJS+DJVgspvgAwy93WyEKKANns +FdE0cfJbZIf2J9K364awkL8p2yGeNozjIC+VI1FsG8Kk1ebYAkNnoP6bUANEf7vk +ctXR5NqPkhRk+10UEBJKlQbJZQgpyiGjJjgRySffcGcE/cpIMn9jskV0MVBPh9kg +cNIhcLHWEJ0zXXiDkW1Vguza5GJjx4FG1xllcipDGZC41yNNTBzgRKlmZ6zucXkn +Jnhtcg71XUsjtXx8ZekXxjoLDd1eHlHDhrjsf8cnSqVG6GotGcGHo8uZk4dkolUU +TLdDpZPX59JOeUDKZZlGPT96gHqIaswe5WszRvRQwNUfCbjNii6hJ+tdc6foawrl +V4IqsPziVFJW8KupEsYjlgcknOC8RqW0IATaCZNj5dQuwn7FMe21FXSGF7mz8yaK +HQJq2ho/6LrxBG2UUVTiWrRZgx1g0C1zzAe1Joz518aIke+Az10PoWDLRdRCItGx +cB390LcwkDrGSG1n5TLaj9vjqOMdICWiHOFMuaT2xj9cWA27xrJ3ARaRnxcGDbdA +PsyPjpxL4J1+mx4Fq4gi+tMoG1cUZEo+JCw4TSFpAHMu0FUtdPIV6JRDPkAqxsa5 +alveoswYUFRdTiqFbPaSiykZfufqSuAiKyW892bPd5pBdPI8FA10afVQg83NLyHb +IkaK0PdRGpVX8gWLGhntO0XoNsJufvtXIgAfBlOprpPGj3EqMUWS545t5pkiwIP8 +79xXZndPojYx+6ETjeXKo5V9AQxkcDtTQmiAx7udqAA1aZgMqGfYQ+Wqz5XgUZWk +Fz9CnbgEztN5ecjTihYykuDXou7XN0wvrLh7vkX28RgznHs3piTZvECrAOnDN4ur +2LbzXoFOsBRrBz4f7ML2RCKVu7Pmb9b5cGW6CoNlqg4TL4MTI1OLQBb6zi/8TQT4 +69isxTbCFVdIOOxVs7Qeuq3SQgYXDXPIV6a+lk2p8sD7eiEc9clwqYKQtfEM1HkQ +voGm6VxhnHd5mqTDNyZXN8lSLPoI/9BfxmHA9Ha+/N5Oz6tRmXHH33701s8GVhkT +UwttdFlIGZtTBS2dMlTT5SxTi2Q+1GR744AJFMz+FkZja3Fp+PnLJ/aIVLxFs84C +yJTuQFv5QgLC/7DYLOsof17JJgGZpw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy +NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y +LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ +TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y +TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 +LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW +I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw +nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy +NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY +dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 +WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS +v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v +UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu +IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC +W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8xCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh +c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05 +NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYD +VQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMSBQdWJsaWMgUHJp +bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3noaACpEO+jglr0aIguVzqKCbJF0N +H8xlbgyw0FaEGIeaBpsQoXPftFg5a27B9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR +4k5FVmkfeAKA2txHkSm7NsljXMXg1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATAN +BgkqhkiG9w0BAQIFAAOBgQBMP7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZo +EWx8QszznC7EBz8UsA9P/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5 +FvjqBUuUfx3CHMjjt/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89Fx +lA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK +VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm +Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J +h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul +uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68 +DzFc6PLZ +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 +nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO +8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV +ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb +PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 +6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr +n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a +qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 +wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 +ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs +pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 +E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAyIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZMJaLtVRKXxaeAufqDwSCg+i8VDXyh +YGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvEerf4Zh+AVPy3wo5ZShRXRtGak75BkQO7 +FYCTXOvnzAhsPz6zSvz/S2wj1VCCJkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBAIobK/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxg +J8pFUs4W7z8GZOeUaHxgMxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Nc +r6Pc5iaAIzy4RHT3Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns +YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y +aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe +Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj +IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx +KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM +HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw +DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC +AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji +nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX +rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn +jBJ7xUS0rg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy +aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp +Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV +BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp +Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g +Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU +J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO +JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY +wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o +koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN +qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E +Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe +xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u +7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU +sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI +sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP +cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDM +HO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtK +qsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwj +cSGIL4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y +cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckztImRP +T8qAkbYp +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1 +GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ ++mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd +U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm +NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY +ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ +ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1 +CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq +g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c +2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/ +bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD +VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0 +MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNV +BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2Vy +dmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJ +ADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII +0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphI +uR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZI +hvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3 +YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc +1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E +jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo +ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI +ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu +Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg +AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 +HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA +uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa +TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg +xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q +CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x +O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs +6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjTCCAXWgAwIBAgIDAQAhMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMxNTNaFw0xMjA3MTIxNjMxNTNaMEMxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xFzAVBgNVBAMTDkNlcnR1bSBM +ZXZlbCBJMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCl73pZ9DFcn7Qy0qBZ +K+So18cav7drUrJ8SiYOlDDVskt81+eIcL/4FelTSGjuAOvYdmm+HGYG998RPB0i +Z+Ak67vXFJ537vRWOcu6aMjNuAwu8BOdc5eSgB0Y8X4+3LOYfugtaZa8mrEQ8Hit +0yLE9UBcU9J+4PmkVGecmZ8jZQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBBQUAA4IBAQAlDS4aTmgK0YgmUvt/3zN7G2/ZrtBBCtONlUvC69c7 +TmLJWJ842y2AH7ryNXXkcsn6p0ZBTrTJ2tA2y/j2PXJeXrCkK/qAJIpM0l4u0MT7 +enY5akasduHp2NXMP9vDlgMy7elU2s3nkOT79gfh5XttC+5D/x4JDNi1DMAA9hk1 +6DK4zWmDVfjkiP/G3fEndtJgNDQsyqnaQ3E3bljv3f1KJTjZUvtA2Ml6MP2hFRhg +ZPsxuhW8QXidQYNiua1h7XUUiPiERLDLWZmfY6dxGrHXjSTx3shHNaQM0qkDs9gS +6UK8uWJN2bf2YBnvGmzy0IQvx5wDCH7h8AdaBD6DgIG1 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjjCCAXagAwIBAgIDAQAiMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMyMDNaFw0xMjA3MTIxNjMyMDNaMEQxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xGDAWBgNVBAMTD0NlcnR1bSBM +ZXZlbCBJSTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyMQSaN5fA94hNE46 +bMKpGUb5yIPEowReGZzGttYBQnC6oUOy+iM3md8WerzXeBKf7iIZEDW2HAp7BKhS +4rMB6taxT07vDtkNfEKwOk6X7dODw6KY4mxnzjmjh5pf2feKKJ3MoZxi2HAz2a6J +vHKFMq8dAlGL2GBtLvzlFp2jwkMCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN +BgkqhkiG9w0BAQUFAAOCAQEAWo3wgy+/0B7UiTCu4Wn1rvGRXIUtbPNp4Bc4PP/i +1q6pPheIe0ooCopuqnDX9maTHhZeNpnApgCUSbyw71EaOremD7HjWXASRUTylhwL +5FdSx+D6MgF2uW9uwZ+NErkeRJYT2aRXe5FBOVIseC4g93Ay0D8Hg50MkAC5pQqW ++8GSszT94NzT7ppIaMtq53PZpUtLGiL3UBZ5vUJ5pE4lLKD7Ce+pXzZevy/MnkMG +D1L7LgjRWL17OcMlASFETyUTajNjvxMy+oM4C22rwHRh2WQrvgw5MO+Q3UyYA1r5 +VrSaqgQ1g06ZcQt+mhzoc2swlOSwm8iis8H6orR8xmCWrA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjzCCAXegAwIBAgIDAQAjMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMyMTdaFw0xMjA3MTIxNjMyMTdaMEUxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xGTAXBgNVBAMTEENlcnR1bSBM +ZXZlbCBJSUkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALZjBbfGFmlsLjPe +pWaDwG0LqhF11lWKabaHi1sQhK3qomHY7Em7qpL11dUQ1vsMcnnpzz/J0AEH6KDh ++yAyXV1SE/tVToLYYByZK+JGacLYIYF9aCwV8AhqyzOGurO5QX6vLboXB2WNnwmX +hyNVKUgnUVy4ktAR2qZJIw5Bjsn/AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAIsLt3vKCZqd/gui45ovm3FSO6FLjzzq4pagPvbN +nZ39HrhRaCpqkHDAj71L5L27U3eW2D4ILL0iUmZadbC4i3at/PUL9mjhGlajcCN8 +EF6IXGT87Tbcii735jRaaXSbEY4YhNOg9DPBoD4uJMkA8Z0Y/6lYmk4S6KUMCzzt +t5zZBiWjdd08yFi5VGMvpE74KVOMdMa3JNVaR0XvT0Q8yXo1XKCrY9OFIxnhVgDb +hzr9fwjKWDwu8kxhT9khAETm0BU2Buu+CTasaJdT/bBR2YEx9qcN7XyXTeDtkOO5 +QeGSqFgzquwjWEbKhf7l/e+efdRCg+ikH3O5snHB6iS+dgg= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICjjCCAXagAwIBAgIDAQAkMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD +QTAeFw0wMjA3MTIxNjMyMzVaFw0xMjA3MTIxNjMyMzVaMEQxCzAJBgNVBAYTAlBM +MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xGDAWBgNVBAMTD0NlcnR1bSBM +ZXZlbCBJVjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmyb1lKqCAKE4juAy +6lpVNUl6aJ2DuWPSiJ3BBk3/6ty6I4Lr2Dpy1b1vjVelhaFsVKEDgK2JyQlk9XMq +LPZI2Ql166mJiPKFg77aY/W78EcQfGyjnRvVcs0tG40mAs/p84OEpFcVe/RSqDrD +/D7R01u+Wj5xLl0PUsFplIGDbikCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN +BgkqhkiG9w0BAQUFAAOCAQEAPS99JujKGVRfa50TKfieq+uK1SxjidErYaZTb3cJ +NNfQDYn6nk4lnrnab5EUVhO/NegP2yIu3YOnZGfxFDhvVozMTKKAB5r5XKOvzsP9 +9C9578PVMLozucfUMCSwau7Z4l5uuQOHuzjzlVLCibbbf4RwfvZ7hh5sB5c0pNbw +RQq64RXQUUEvul/W9gUeT9ISHOsASGTq+HJ5i7vNARjukEAXW/maqs9vyTWWbGVI +1FSOnVyteymq4Xk+9YlIyNPNyacgnsMnU72XKBLDS0KJdhIWALFAZI4dSh5WZNuW +ZguUnEmeH81lLbR+p/N3iuN8+oSo8UXik92jxeUY2tQJUA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF +MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU +QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI +MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENBMSkwJwYJKoZIhvcN +AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla +Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy +ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y +IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1 +c3RDZW50ZXIgQ2xhc3MgMiBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA +dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0y +AClxgwENv4wB3NrGrTmkqYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDw +TFXlay3HhQswHJJOgtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8 +/vhYnvgpjbB7zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3 +LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G +CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+WLDO/ +jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xRT3h2oNms +Gb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/AcASZ4smZHcFFk +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRF +MRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFU +QyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJI +MSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENBMSkwJwYJKoZIhvcN +AQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTla +Fw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVy +ZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9y +IFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1 +c3RDZW50ZXIgQ2xhc3MgMyBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVA +dHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUF +Lg2N7KBAahwOJ6ZQkmtQGwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGw +Dtf7pBc9r6tpepYnv68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDW +w1Krj10nnGvAo+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3 +LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G +CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4iJIE +Tb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yCGdHHsbHD +2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQSCdS7kjXvD9s0 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIG2jCCBcKgAwIBAgIDFc/9MA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJE +RTEhMB8GA1UEChMYRGV1dHNjaGVzIEZvcnNjaHVuZ3NuZXR6MRYwFAYDVQQLEw1E +Rk4tQ0VSVCBHbWJIMRAwDgYDVQQLEwdERk4tUENBMS0wKwYDVQQDEyRERk4gVG9w +bGV2ZWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEmNl +cnRpZnlAcGNhLmRmbi5kZTAeFw0wMTEyMDExMjExMTZaFw0xMDAxMzExMjExMTZa +MIGsMQswCQYDVQQGEwJERTEhMB8GA1UEChMYRGV1dHNjaGVzIEZvcnNjaHVuZ3Nu +ZXR6MRYwFAYDVQQLEw1ERk4tQ0VSVCBHbWJIMRAwDgYDVQQLEwdERk4tUENBMS0w +KwYDVQQDEyRERk4gVG9wbGV2ZWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxITAf +BgkqhkiG9w0BCQEWEmNlcnRpZnlAcGNhLmRmbi5kZTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMF5rhMt6zmhxK5oWPwT2FG7Up7T5DovHSD/YKPIRxsv +DWmC4dTzByIBLnOmEflk+5KAqAYao6eY1qF0hR4WiS4DjCsn7l3zNo/4i2eF4EmG +EksBygb4tRlTThcO7heFX+Du5qFoks+ONqa70RlwOr2l53KVwjMXBCtCLFSKRLVu +xeh5+Smkm+FuOmwEugndM2n74Djjyf9DCOaHGZrHwVDh+Vpy5Ny4bKCSboujRxd5 +NxsStUshDVbTeS3B8TuzAJbywYWEE7erox+7WTfQr8ivSCBhrNJ36VRjAb8hiV9I +uy2TmJYo2oPyC8a3eM3xj9Ku2IW3tS2zpfiIzt9xvFMCAwEAAaOCAwEwggL9MA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAYL+rX4SHijILELPs+g0MTRf33QMIHb +BgNVHSMEgdMwgdCAFAYL+rX4SHijILELPs+g0MTRf33QoYGypIGvMIGsMQswCQYD +VQQGEwJERTEhMB8GA1UEChMYRGV1dHNjaGVzIEZvcnNjaHVuZ3NuZXR6MRYwFAYD +VQQLEw1ERk4tQ0VSVCBHbWJIMRAwDgYDVQQLEwdERk4tUENBMS0wKwYDVQQDEyRE +Rk4gVG9wbGV2ZWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxITAfBgkqhkiG9w0B +CQEWEmNlcnRpZnlAcGNhLmRmbi5kZYIDFc/9MAsGA1UdDwQEAwIBBjARBglghkgB +hvhCAQEEBAMCAAcwgaUGA1UdHwSBnTCBmjBLoEmgR4ZFaHR0cDovL3d3dy5kZm4t +cGNhLmRlL2NlcnRpZmljYXRpb24veDUwOS9nMS9kYXRhL2NybHMvcm9vdC1jYS1j +cmwuY3J4MEugSaBHhkVodHRwOi8vd3d3LmRmbi1wY2EuZGUvY2VydGlmaWNhdGlv +bi94NTA5L2cxL2RhdGEvY3Jscy9yb290LWNhLWNybC5jcmwwOAYJYIZIAYb4QgED +BCsWKWh0dHBzOi8vd3d3LmRmbi1wY2EuZGUvY2dpL2NoZWNrLXJldi5jZ2k/MEsG +CWCGSAGG+EIBCAQ+FjxodHRwOi8vd3d3LmRmbi1wY2EuZGUvY2VydGlmaWNhdGlv +bi9wb2xpY2llcy94NTA5cG9saWN5Lmh0bWwwOAYJYIZIAYb4QgENBCsWKVRoZSBE +Rk4gVG9wLUxldmVsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MGQGA1UdIARdMFsw +WQYLKwYBBAHZGoIsAQEwSjBIBggrBgEFBQcCARY8aHR0cDovL3d3dy5kZm4tcGNh +LmRlL2NlcnRpZmljYXRpb24vcG9saWNpZXMveDUwOXBvbGljeS5odG1sMA0GCSqG +SIb3DQEBBQUAA4IBAQAmbai6JMt7nkuavyvxKzLGn04Gyt0zKrp8zmERp4inktvY +7p+vkaomYu2QYC7cHq0tlrPXQQhhetjiXGb+36aJtHDkEA0NwrJzYnHgPsvx7z0w +ysENP4wxf97KsSWm07RY+f6/gIQF7Je7CW30Rzq7N6R0NMBs32mJgdn3ntqlFNw3 +Nbs050FEjPNq54RdawlJo85x+w+QJd7uQM4yZjHpRhvwgte9Ge1UqCUdpMsLHzeM +KJ0B9GhwIIqOJCMiPgKjcUBrn6ehSX70POvXvjjE2+FzhPGTyTkS474d2UCAnL9q +hPrdWXzBjOumOjhJutT1aecm9eljlshmh1cNen00 +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCB +ozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3Qt +TmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5WhcNMTkwNzA5MTg1 +NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0 +IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYD +VQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VS +Rmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCz+5Gh5DZVhawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2 +N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCH +iZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hARe +YFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1 +axwiP8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6g +yN7igEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD +AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPh +ahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9V +VE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0GCSqGSIb3DQEB +BQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3Y +IWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6Lzs +QCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4 +ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qM +YEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUb +QErNaLly7HF27FSOH4UMAWr6pjisH8SE +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp +ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow +fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV +BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM +cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S +HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996 +CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk +3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz +6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV +HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv +Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw +Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww +DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0 +5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI +gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ +aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl +izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0 +aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla +MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD +VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW +fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt +TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL +fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW +1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7 +kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G +A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v +ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo +dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu +Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/ +HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS +jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+ +xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn +dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx +MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG +29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk +oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk +3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL +qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN +nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX +ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H +DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO +TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv +kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w +zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB +lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt +SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG +A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe +MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v +d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh +cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn +0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ +M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a +MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd +oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI +DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy +oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 +dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy +bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF +BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli +CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE +CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t +3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS +KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB +rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt +Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa +Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV +BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l +dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE +AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B +YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 +hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l +L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm +SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM +1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws +6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw +Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 +aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u +7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 +xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ +rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim +eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk +USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB +lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt +T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc +BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3 +dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP +HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO +KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo +5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+ +pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb +kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC +AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov +L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV +HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN +AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw +NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB +mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU +4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5 +81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR +Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD +EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X +DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw +DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u +c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr +TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA +OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC +2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW +RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P +AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW +ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0 +YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz +b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO +ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB +IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs +b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s +YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg +a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g +SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0 +aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg +YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg +Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY +ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g +pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4 +Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV +MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe +TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0 +dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0 +N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC +dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu +MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL +b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD +zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi +3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8 +WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY +Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi +NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC +ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4 +QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0 +YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz +aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm +ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg +ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs +amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv +IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3 +Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6 +ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1 +YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg +dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs +b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G +CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO +xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP +0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ +QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk +f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK +8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD +EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05 +OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l +dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK +gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX +iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc +Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E +BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G +SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu +b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh +bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv +Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln +aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0 +IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph +biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo +ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP +UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj +YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA +bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06 +sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa +n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS +NitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiTCCAfKgAwIBAgIEN4dnrDANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJi +ZTERMA8GA1UEChMIQmVsZ2Fjb20xDDAKBgNVBAsTA01UTTEkMCIGA1UEAxMbQmVs +Z2Fjb20gRS1UcnVzdCBQcmltYXJ5IENBMR8wHQYKCZImiZPyLGQBAxQPaW5mb0Bl +LXRydXN0LmJlMB4XDTk4MTEwNDEzMDQzOVoXDTEwMDEyMTEzMDQzOVowdTELMAkG +A1UEBhMCYmUxETAPBgNVBAoTCEJlbGdhY29tMQwwCgYDVQQLEwNNVE0xJDAiBgNV +BAMTG0JlbGdhY29tIEUtVHJ1c3QgUHJpbWFyeSBDQTEfMB0GCgmSJomT8ixkAQMU +D2luZm9AZS10cnVzdC5iZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqtm5 +s9VPak3FQdB7BGFqi3GBB9pk41huJ1XCrc4XsPz6ko0I8Bxy/7LDMf7gaoeXTMxD +V6coeTq1g12kHWrxasU+FCIdWQZv8KYxd9ywSTjmywwP/qpyNIjaKDohWu50Kxuk +21sTFrVzX8OujNLAPj2wy/Dsi4YLwsFEGFpjqNUCAwEAAaMmMCQwDwYDVR0TBAgw +BgEB/wIBATARBglghkgBhvhCAQEEBAMCAAcwDQYJKoZIhvcNAQEFBQADgYEAerKx +pbF9M+nC4RvO05OMfwH9Gx1amq6rB1Ev7Ymr3VBCux//SrWknLFhKQpM6oNZSY2v +hmnXgaxHqqRxblnvynxqblSK2qiSyfVms3lf1IsBniFjRjWTpcJfImIDcB1jI+hr +SB0jECfY9t9HorrsgFBKbMRwpnrkdCJ/9oRiMn8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQGEwJE +SzEMMAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0wMzAyMTEw +ODM5MzBaFw0zNzAyMTEwOTA5MzBaMDExCzAJBgNVBAYTAkRLMQwwCgYDVQQKEwNU +REMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSjhFuHnEz9pPPEXyG9VhDr +2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8z3sM8W9Hpg1DTeLpHTk0zY0s +2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJHhNlktxmW/OwZ5LKXJk5KTMuPJItU +GBxIYXvViGjaXbXqzRowwYCDdlCqT9HU3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKj +dGqPqcNiKXEx5TukYBdedObaE+3pHx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+r +TpPGWOmlgtt3xDqZsXKVSQTwtyv6e1mO3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgewGA1UdIASB5DCB4TCB3gYIKoFQgSkB +AQEwgdEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5kay9yZXBv +c2l0b3J5MIGdBggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRl +ciBmcmEgZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEu +MS4xLiBDZXJ0aWZpY2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIg +T0lEIDEuMi4yMDguMTY5LjEuMS4xLjARBglghkgBhvhCAQEEBAMCAAcwgYEGA1Ud +HwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEMMAoGA1UEChMDVERDMRQwEgYD +VQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYmaHR0cDovL2Ny +bC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0QBCQwIoAPMjAwMzAy +MTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0jBBgwFoAUYLWF7FZkfhIZ +J2cdUBVLc647+RIwHQYDVR0OBBYEFGC1hexWZH4SGSdnHVAVS3OuO/kSMB0GCSqG +SIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEACrom +JkbTc6gJ82sLMJn9iuFXehHTuJTXCRBuo7E4A9G28kNBKWKnctj7fAXmMXAnVBhO +inxO5dHKjHiIzxvTkIvmI/gLDjNDfZziChmPyQE+dF10yYscA+UYyAFMP8uXBV2Y +caaYb7Z8vTd/vuGTJW1v8AqtFxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9AOoB +mbgGglGBTvH1tJFUuSN6AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQ +YqbsFbS1AoLbrIyigfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9 +BKNDLdr8C2LqL19iUw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJE +SzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQg +Um9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNV +BAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNVBAsTFFREQyBJbnRl +cm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxLhA +vJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20jxsNu +Zp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a +0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc1 +4izbSysseLlJ28TQx5yc5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGN +eGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcD +R0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUG +A1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIElu +dGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxME +Q1JMMTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3 +WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw +HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJ +KoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQBO +Q8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540mgwV5dOy0uaOX +wTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm89 +9qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0 +jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38 +aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx +ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0 +b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD +EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz +aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w +MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G +A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh +Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l +dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh +bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq +eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe +r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5 +3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd +vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l +mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC +wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg +hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0 +TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh +biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg +ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg +dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6 +b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl +c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0 +ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3 +dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu +ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo +ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3 +Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u +ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA +A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ +MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+ +NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR +VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY +83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3 +macqaJVmlaut74nLYKkGEsaUR+ko +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs +IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg +R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A +PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8 +Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL +TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL +5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7 +S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe +2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap +EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td +EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv +/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN +A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0 +abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF +I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz +4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri new file mode 100644 index 0000000..196e19d --- /dev/null +++ b/src/network/ssl/ssl.pri @@ -0,0 +1,33 @@ +# OpenSSL support; compile in QSslSocket. +contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { + 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 + + # Include Qt's default CA bundle + RESOURCES += network.qrc + + # Add optional SSL libs + LIBS += $$OPENSSL_LIBS +} |