summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShane Kearns <shane.kearns@accenture.com>2012-01-11 16:52:22 +0000
committerShane Kearns <shane.kearns@accenture.com>2012-04-20 12:28:08 +0200
commit3e631094cf4f799b2f3b4296598241c3c6274f6a (patch)
tree11c92f11b96747bc049703dc82f13f80fa247a99
parent2583c254c3961c833e0bf4a556682e77f3b6f8ec (diff)
Add QHttp sources from Qt
Change-Id: Ia9bf68bd30498b3218de84b31e4176524fab62d8 Reviewed-by: Shane Kearns <shane.kearns@accenture.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/qhttp/qhttp.cpp3155
-rw-r--r--src/qhttp/qhttp.h315
-rw-r--r--src/qhttp/qhttpauthenticator.cpp1426
-rw-r--r--src/qhttp/qhttpauthenticator_p.h116
-rw-r--r--src/qhttp/qringbuffer_p.h451
5 files changed, 5463 insertions, 0 deletions
diff --git a/src/qhttp/qhttp.cpp b/src/qhttp/qhttp.cpp
new file mode 100644
index 0000000..115c48d
--- /dev/null
+++ b/src/qhttp/qhttp.cpp
@@ -0,0 +1,3155 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHTTP_DEBUG
+
+#include <qplatformdefs.h>
+#include "qhttp.h"
+
+#ifndef QT_NO_HTTP
+# include "private/qobject_p.h"
+# include "qtcpsocket.h"
+# include "qsslsocket.h"
+# include "qtextstream.h"
+# include "qmap.h"
+# include "qlist.h"
+# include "qstring.h"
+# include "qstringlist.h"
+# include "qbuffer.h"
+# include "private/qringbuffer_p.h"
+# include "qcoreevent.h"
+# include "qurl.h"
+# include "qnetworkproxy.h"
+# include "qauthenticator.h"
+# include "qauthenticator_p.h"
+# include "qdebug.h"
+# include "qtimer.h"
+#endif
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNormalRequest;
+class QHttpRequest
+{
+public:
+ QHttpRequest() : finished(false)
+ { id = idCounter.fetchAndAddRelaxed(1); }
+ virtual ~QHttpRequest()
+ { }
+
+ virtual void start(QHttp *) = 0;
+ virtual bool hasRequestHeader();
+ virtual QHttpRequestHeader requestHeader();
+
+ virtual QIODevice *sourceDevice() = 0;
+ virtual QIODevice *destinationDevice() = 0;
+
+ int id;
+ bool finished;
+
+private:
+ static QBasicAtomicInt idCounter;
+};
+
+class QHttpPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QHttp)
+
+ inline QHttpPrivate()
+ : socket(0), reconnectAttempts(2),
+ deleteSocket(0), state(QHttp::Unconnected),
+ error(QHttp::NoError), port(0), mode(QHttp::ConnectionModeHttp),
+ toDevice(0), postDevice(0), bytesDone(0), chunkedSize(-1),
+ repost(false), pendingPost(false)
+ {
+ }
+
+ inline ~QHttpPrivate()
+ {
+ while (!pending.isEmpty())
+ delete pending.takeFirst();
+
+ if (deleteSocket)
+ delete socket;
+ }
+
+ // private slots
+ void _q_startNextRequest();
+ void _q_slotReadyRead();
+ void _q_slotConnected();
+ void _q_slotError(QAbstractSocket::SocketError);
+ void _q_slotClosed();
+ void _q_slotBytesWritten(qint64 numBytes);
+#ifndef QT_NO_OPENSSL
+ void _q_slotEncryptedBytesWritten(qint64 numBytes);
+#endif
+ void _q_slotDoFinished();
+ void _q_slotSendRequest();
+ void _q_continuePost();
+
+ int addRequest(QHttpNormalRequest *);
+ int addRequest(QHttpRequest *);
+ void finishedWithSuccess();
+ void finishedWithError(const QString &detail, int errorCode);
+
+ void init();
+ void setState(int);
+ void closeConn();
+ void setSock(QTcpSocket *sock);
+
+ void postMoreData();
+
+ QTcpSocket *socket;
+ int reconnectAttempts;
+ bool deleteSocket;
+ QList<QHttpRequest *> pending;
+
+ QHttp::State state;
+ QHttp::Error error;
+ QString errorString;
+
+ QString hostName;
+ quint16 port;
+ QHttp::ConnectionMode mode;
+
+ QByteArray buffer;
+ QIODevice *toDevice;
+ QIODevice *postDevice;
+
+ qint64 bytesDone;
+ qint64 bytesTotal;
+ qint64 chunkedSize;
+
+ QHttpRequestHeader header;
+
+ bool readHeader;
+ QString headerStr;
+ QHttpResponseHeader response;
+
+ QRingBuffer rba;
+
+#ifndef QT_NO_NETWORKPROXY
+ QNetworkProxy proxy;
+ QAuthenticator proxyAuthenticator;
+#endif
+ QAuthenticator authenticator;
+ bool repost;
+ bool hasFinishedWithError;
+ bool pendingPost;
+ QTimer post100ContinueTimer;
+};
+
+QBasicAtomicInt QHttpRequest::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+bool QHttpRequest::hasRequestHeader()
+{
+ return false;
+}
+
+QHttpRequestHeader QHttpRequest::requestHeader()
+{
+ return QHttpRequestHeader();
+}
+
+/****************************************************
+ *
+ * QHttpNormalRequest
+ *
+ ****************************************************/
+
+class QHttpNormalRequest : public QHttpRequest
+{
+public:
+ QHttpNormalRequest(const QHttpRequestHeader &h, QIODevice *d, QIODevice *t) :
+ header(h), to(t)
+ {
+ is_ba = false;
+ data.dev = d;
+ }
+
+ QHttpNormalRequest(const QHttpRequestHeader &h, QByteArray *d, QIODevice *t) :
+ header(h), to(t)
+ {
+ is_ba = true;
+ data.ba = d;
+ }
+
+ ~QHttpNormalRequest()
+ {
+ if (is_ba)
+ delete data.ba;
+ }
+
+ void start(QHttp *);
+ bool hasRequestHeader();
+ QHttpRequestHeader requestHeader();
+ inline void setRequestHeader(const QHttpRequestHeader &h) { header = h; }
+
+ QIODevice *sourceDevice();
+ QIODevice *destinationDevice();
+
+protected:
+ QHttpRequestHeader header;
+
+private:
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+ QIODevice *to;
+};
+
+void QHttpNormalRequest::start(QHttp *http)
+{
+ if (!http->d_func()->socket)
+ http->d_func()->setSock(0);
+ http->d_func()->header = header;
+
+ if (is_ba) {
+ http->d_func()->buffer = *data.ba;
+ if (http->d_func()->buffer.size() >= 0)
+ http->d_func()->header.setContentLength(http->d_func()->buffer.size());
+
+ http->d_func()->postDevice = 0;
+ } else {
+ http->d_func()->buffer = QByteArray();
+
+ if (data.dev && (data.dev->isOpen() || data.dev->open(QIODevice::ReadOnly))) {
+ http->d_func()->postDevice = data.dev;
+ if (http->d_func()->postDevice->size() >= 0)
+ http->d_func()->header.setContentLength(http->d_func()->postDevice->size());
+ } else {
+ http->d_func()->postDevice = 0;
+ }
+ }
+
+ if (to && (to->isOpen() || to->open(QIODevice::WriteOnly)))
+ http->d_func()->toDevice = to;
+ else
+ http->d_func()->toDevice = 0;
+
+ http->d_func()->reconnectAttempts = 2;
+ http->d_func()->_q_slotSendRequest();
+}
+
+bool QHttpNormalRequest::hasRequestHeader()
+{
+ return true;
+}
+
+QHttpRequestHeader QHttpNormalRequest::requestHeader()
+{
+ return header;
+}
+
+QIODevice *QHttpNormalRequest::sourceDevice()
+{
+ if (is_ba)
+ return 0;
+ return data.dev;
+}
+
+QIODevice *QHttpNormalRequest::destinationDevice()
+{
+ return to;
+}
+
+/****************************************************
+ *
+ * QHttpPGHRequest
+ * (like a QHttpNormalRequest, but for the convenience
+ * functions put(), get() and head() -- i.e. set the
+ * host header field correctly before sending the
+ * request)
+ *
+ ****************************************************/
+
+class QHttpPGHRequest : public QHttpNormalRequest
+{
+public:
+ QHttpPGHRequest(const QHttpRequestHeader &h, QIODevice *d, QIODevice *t) :
+ QHttpNormalRequest(h, d, t)
+ { }
+
+ QHttpPGHRequest(const QHttpRequestHeader &h, QByteArray *d, QIODevice *t) :
+ QHttpNormalRequest(h, d, t)
+ { }
+
+ ~QHttpPGHRequest()
+ { }
+
+ void start(QHttp *);
+};
+
+void QHttpPGHRequest::start(QHttp *http)
+{
+ if (http->d_func()->port && http->d_func()->port != 80)
+ header.setValue(QLatin1String("Host"), http->d_func()->hostName + QLatin1Char(':') + QString::number(http->d_func()->port));
+ else
+ header.setValue(QLatin1String("Host"), http->d_func()->hostName);
+ QHttpNormalRequest::start(http);
+}
+
+/****************************************************
+ *
+ * QHttpSetHostRequest
+ *
+ ****************************************************/
+
+class QHttpSetHostRequest : public QHttpRequest
+{
+public:
+ QHttpSetHostRequest(const QString &h, quint16 p, QHttp::ConnectionMode m)
+ : hostName(h), port(p), mode(m)
+ { }
+
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+
+private:
+ QString hostName;
+ quint16 port;
+ QHttp::ConnectionMode mode;
+};
+
+void QHttpSetHostRequest::start(QHttp *http)
+{
+ http->d_func()->hostName = hostName;
+ http->d_func()->port = port;
+ http->d_func()->mode = mode;
+
+#ifdef QT_NO_OPENSSL
+ if (mode == QHttp::ConnectionModeHttps) {
+ // SSL requested but no SSL support compiled in
+ http->d_func()->finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTPS connection requested but SSL support not compiled in")),
+ QHttp::UnknownError);
+ return;
+ }
+#endif
+
+ http->d_func()->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * QHttpSetUserRequest
+ *
+ ****************************************************/
+
+class QHttpSetUserRequest : public QHttpRequest
+{
+public:
+ QHttpSetUserRequest(const QString &userName, const QString &password) :
+ user(userName), pass(password)
+ { }
+
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+
+private:
+ QString user;
+ QString pass;
+};
+
+void QHttpSetUserRequest::start(QHttp *http)
+{
+ http->d_func()->authenticator.setUser(user);
+ http->d_func()->authenticator.setPassword(pass);
+ http->d_func()->finishedWithSuccess();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+
+/****************************************************
+ *
+ * QHttpSetProxyRequest
+ *
+ ****************************************************/
+
+class QHttpSetProxyRequest : public QHttpRequest
+{
+public:
+ inline QHttpSetProxyRequest(const QNetworkProxy &proxy)
+ {
+ this->proxy = proxy;
+ }
+
+ inline void start(QHttp *http)
+ {
+ http->d_func()->proxy = proxy;
+ QString user = proxy.user();
+ if (!user.isEmpty())
+ http->d_func()->proxyAuthenticator.setUser(user);
+ QString password = proxy.password();
+ if (!password.isEmpty())
+ http->d_func()->proxyAuthenticator.setPassword(password);
+ http->d_func()->finishedWithSuccess();
+ }
+
+ inline QIODevice *sourceDevice()
+ { return 0; }
+ inline QIODevice *destinationDevice()
+ { return 0; }
+private:
+ QNetworkProxy proxy;
+};
+
+#endif // QT_NO_NETWORKPROXY
+
+/****************************************************
+ *
+ * QHttpSetSocketRequest
+ *
+ ****************************************************/
+
+class QHttpSetSocketRequest : public QHttpRequest
+{
+public:
+ QHttpSetSocketRequest(QTcpSocket *s) : socket(s)
+ { }
+
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+
+private:
+ QTcpSocket *socket;
+};
+
+void QHttpSetSocketRequest::start(QHttp *http)
+{
+ http->d_func()->setSock(socket);
+ http->d_func()->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * QHttpCloseRequest
+ *
+ ****************************************************/
+
+class QHttpCloseRequest : public QHttpRequest
+{
+public:
+ QHttpCloseRequest()
+ { }
+ void start(QHttp *);
+
+ QIODevice *sourceDevice()
+ { return 0; }
+ QIODevice *destinationDevice()
+ { return 0; }
+};
+
+void QHttpCloseRequest::start(QHttp *http)
+{
+ http->d_func()->closeConn();
+}
+
+class QHttpHeaderPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpHeader)
+public:
+ inline virtual ~QHttpHeaderPrivate() {}
+
+ QList<QPair<QString, QString> > values;
+ bool valid;
+ QHttpHeader *q_ptr;
+};
+
+/****************************************************
+ *
+ * QHttpHeader
+ *
+ ****************************************************/
+
+/*!
+ \class QHttpHeader
+ \obsolete
+ \brief The QHttpHeader class contains header information for HTTP.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ In most cases you should use the more specialized derivatives of
+ this class, QHttpResponseHeader and QHttpRequestHeader, rather
+ than directly using QHttpHeader.
+
+ QHttpHeader provides the HTTP header fields. A HTTP header field
+ consists of a name followed by a colon, a single space, and the
+ field value. (See RFC 1945.) Field names are case-insensitive. A
+ typical header field looks like this:
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 0
+
+ In the API the header field name is called the "key" and the
+ content is called the "value". You can get and set a header
+ field's value by using its key with value() and setValue(), e.g.
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 1
+
+ Some fields are so common that getters and setters are provided
+ for them as a convenient alternative to using \l value() and
+ \l setValue(), e.g. contentLength() and contentType(),
+ setContentLength() and setContentType().
+
+ Each header key has a \e single value associated with it. If you
+ set the value for a key which already exists the previous value
+ will be discarded.
+
+ \sa QHttpRequestHeader QHttpResponseHeader
+*/
+
+/*!
+ \fn int QHttpHeader::majorVersion() const
+
+ Returns the major protocol-version of the HTTP header.
+*/
+
+/*!
+ \fn int QHttpHeader::minorVersion() const
+
+ Returns the minor protocol-version of the HTTP header.
+*/
+
+/*!
+ Constructs an empty HTTP header.
+*/
+QHttpHeader::QHttpHeader()
+ : d_ptr(new QHttpHeaderPrivate)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = true;
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+QHttpHeader::QHttpHeader(const QHttpHeader &header)
+ : d_ptr(new QHttpHeaderPrivate)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = header.d_func()->valid;
+ d->values = header.d_func()->values;
+}
+
+/*!
+ Constructs a HTTP header for \a str.
+
+ This constructor parses the string \a str for header fields and
+ adds this information. The \a str should consist of one or more
+ "\r\n" delimited lines; each of these lines should have the format
+ key, colon, space, value.
+*/
+QHttpHeader::QHttpHeader(const QString &str)
+ : d_ptr(new QHttpHeaderPrivate)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = true;
+ parse(str);
+}
+
+/*! \internal
+ */
+QHttpHeader::QHttpHeader(QHttpHeaderPrivate &dd, const QString &str)
+ : d_ptr(&dd)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = true;
+ if (!str.isEmpty())
+ parse(str);
+}
+
+/*! \internal
+ */
+QHttpHeader::QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header)
+ : d_ptr(&dd)
+{
+ Q_D(QHttpHeader);
+ d->q_ptr = this;
+ d->valid = header.d_func()->valid;
+ d->values = header.d_func()->values;
+}
+/*!
+ Destructor.
+*/
+QHttpHeader::~QHttpHeader()
+{
+}
+
+/*!
+ Assigns \a h and returns a reference to this http header.
+*/
+QHttpHeader &QHttpHeader::operator=(const QHttpHeader &h)
+{
+ Q_D(QHttpHeader);
+ d->values = h.d_func()->values;
+ d->valid = h.d_func()->valid;
+ return *this;
+}
+
+/*!
+ Returns true if the HTTP header is valid; otherwise returns false.
+
+ A QHttpHeader is invalid if it was created by parsing a malformed string.
+*/
+bool QHttpHeader::isValid() const
+{
+ Q_D(const QHttpHeader);
+ return d->valid;
+}
+
+/*! \internal
+ Parses the HTTP header string \a str for header fields and adds
+ the keys/values it finds. If the string is not parsed successfully
+ the QHttpHeader becomes \link isValid() invalid\endlink.
+
+ Returns true if \a str was successfully parsed; otherwise returns false.
+
+ \sa toString()
+*/
+bool QHttpHeader::parse(const QString &str)
+{
+ Q_D(QHttpHeader);
+ QStringList lst;
+ int pos = str.indexOf(QLatin1Char('\n'));
+ if (pos > 0 && str.at(pos - 1) == QLatin1Char('\r'))
+ lst = str.trimmed().split(QLatin1String("\r\n"));
+ else
+ lst = str.trimmed().split(QLatin1String("\n"));
+ lst.removeAll(QString()); // No empties
+
+ if (lst.isEmpty())
+ return true;
+
+ QStringList lines;
+ QStringList::Iterator it = lst.begin();
+ for (; it != lst.end(); ++it) {
+ if (!(*it).isEmpty()) {
+ if ((*it)[0].isSpace()) {
+ if (!lines.isEmpty()) {
+ lines.last() += QLatin1Char(' ');
+ lines.last() += (*it).trimmed();
+ }
+ } else {
+ lines.append((*it));
+ }
+ }
+ }
+
+ int number = 0;
+ it = lines.begin();
+ for (; it != lines.end(); ++it) {
+ if (!parseLine(*it, number++)) {
+ d->valid = false;
+ return false;
+ }
+ }
+ return true;
+}
+
+/*! \internal
+*/
+void QHttpHeader::setValid(bool v)
+{
+ Q_D(QHttpHeader);
+ d->valid = v;
+}
+
+/*!
+ Returns the first value for the entry with the given \a key. If no entry
+ has this \a key, an empty string is returned.
+
+ \sa setValue() removeValue() hasKey() keys()
+*/
+QString QHttpHeader::value(const QString &key) const
+{
+ Q_D(const QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ if ((*it).first.toLower() == lowercaseKey)
+ return (*it).second;
+ ++it;
+ }
+ return QString();
+}
+
+/*!
+ Returns all the entries with the given \a key. If no entry
+ has this \a key, an empty string list is returned.
+*/
+QStringList QHttpHeader::allValues(const QString &key) const
+{
+ Q_D(const QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QStringList valueList;
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ if ((*it).first.toLower() == lowercaseKey)
+ valueList.append((*it).second);
+ ++it;
+ }
+ return valueList;
+}
+
+/*!
+ Returns a list of the keys in the HTTP header.
+
+ \sa hasKey()
+*/
+QStringList QHttpHeader::keys() const
+{
+ Q_D(const QHttpHeader);
+ QStringList keyList;
+ QSet<QString> seenKeys;
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ const QString &key = (*it).first;
+ QString lowercaseKey = key.toLower();
+ if (!seenKeys.contains(lowercaseKey)) {
+ keyList.append(key);
+ seenKeys.insert(lowercaseKey);
+ }
+ ++it;
+ }
+ return keyList;
+}
+
+/*!
+ Returns true if the HTTP header has an entry with the given \a
+ key; otherwise returns false.
+
+ \sa value() setValue() keys()
+*/
+bool QHttpHeader::hasKey(const QString &key) const
+{
+ Q_D(const QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ if ((*it).first.toLower() == lowercaseKey)
+ return true;
+ ++it;
+ }
+ return false;
+}
+
+/*!
+ Sets the value of the entry with the \a key to \a value.
+
+ If no entry with \a key exists, a new entry with the given \a key
+ and \a value is created. If an entry with the \a key already
+ exists, the first value is discarded and replaced with the given
+ \a value.
+
+ \sa value() hasKey() removeValue()
+*/
+void QHttpHeader::setValue(const QString &key, const QString &value)
+{
+ Q_D(QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+ while (it != d->values.end()) {
+ if ((*it).first.toLower() == lowercaseKey) {
+ (*it).second = value;
+ return;
+ }
+ ++it;
+ }
+ // not found so add
+ addValue(key, value);
+}
+
+/*!
+ Sets the header entries to be the list of key value pairs in \a values.
+*/
+void QHttpHeader::setValues(const QList<QPair<QString, QString> > &values)
+{
+ Q_D(QHttpHeader);
+ d->values = values;
+}
+
+/*!
+ Adds a new entry with the \a key and \a value.
+*/
+void QHttpHeader::addValue(const QString &key, const QString &value)
+{
+ Q_D(QHttpHeader);
+ d->values.append(qMakePair(key, value));
+}
+
+/*!
+ Returns all the entries in the header.
+*/
+QList<QPair<QString, QString> > QHttpHeader::values() const
+{
+ Q_D(const QHttpHeader);
+ return d->values;
+}
+
+/*!
+ Removes the entry with the key \a key from the HTTP header.
+
+ \sa value() setValue()
+*/
+void QHttpHeader::removeValue(const QString &key)
+{
+ Q_D(QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+ while (it != d->values.end()) {
+ if ((*it).first.toLower() == lowercaseKey) {
+ d->values.erase(it);
+ return;
+ }
+ ++it;
+ }
+}
+
+/*!
+ Removes all the entries with the key \a key from the HTTP header.
+*/
+void QHttpHeader::removeAllValues(const QString &key)
+{
+ Q_D(QHttpHeader);
+ QString lowercaseKey = key.toLower();
+ QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+ while (it != d->values.end()) {
+ if ((*it).first.toLower() == lowercaseKey) {
+ it = d->values.erase(it);
+ continue;
+ }
+ ++it;
+ }
+}
+
+/*! \internal
+ Parses the single HTTP header line \a line which has the format
+ key, colon, space, value, and adds key/value to the headers. The
+ linenumber is \a number. Returns true if the line was successfully
+ parsed and the key/value added; otherwise returns false.
+
+ \sa parse()
+*/
+bool QHttpHeader::parseLine(const QString &line, int)
+{
+ int i = line.indexOf(QLatin1Char(':'));
+ if (i == -1)
+ return false;
+
+ addValue(line.left(i).trimmed(), line.mid(i + 1).trimmed());
+
+ return true;
+}
+
+/*!
+ Returns a string representation of the HTTP header.
+
+ The string is suitable for use by the constructor that takes a
+ QString. It consists of lines with the format: key, colon, space,
+ value, "\r\n".
+*/
+QString QHttpHeader::toString() const
+{
+ Q_D(const QHttpHeader);
+ if (!isValid())
+ return QLatin1String("");
+
+ QString ret = QLatin1String("");
+
+ QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+ while (it != d->values.constEnd()) {
+ ret += (*it).first + QLatin1String(": ") + (*it).second + QLatin1String("\r\n");
+ ++it;
+ }
+ return ret;
+}
+
+/*!
+ Returns true if the header has an entry for the special HTTP
+ header field \c content-length; otherwise returns false.
+
+ \sa contentLength() setContentLength()
+*/
+bool QHttpHeader::hasContentLength() const
+{
+ return hasKey(QLatin1String("content-length"));
+}
+
+/*!
+ Returns the value of the special HTTP header field \c
+ content-length.
+
+ \sa setContentLength() hasContentLength()
+*/
+uint QHttpHeader::contentLength() const
+{
+ return value(QLatin1String("content-length")).toUInt();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-length
+ to \a len.
+
+ \sa contentLength() hasContentLength()
+*/
+void QHttpHeader::setContentLength(int len)
+{
+ setValue(QLatin1String("content-length"), QString::number(len));
+}
+
+/*!
+ Returns true if the header has an entry for the special HTTP
+ header field \c content-type; otherwise returns false.
+
+ \sa contentType() setContentType()
+*/
+bool QHttpHeader::hasContentType() const
+{
+ return hasKey(QLatin1String("content-type"));
+}
+
+/*!
+ Returns the value of the special HTTP header field \c content-type.
+
+ \sa setContentType() hasContentType()
+*/
+QString QHttpHeader::contentType() const
+{
+ QString type = value(QLatin1String("content-type"));
+ if (type.isEmpty())
+ return QString();
+
+ int pos = type.indexOf(QLatin1Char(';'));
+ if (pos == -1)
+ return type;
+
+ return type.left(pos).trimmed();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-type to
+ \a type.
+
+ \sa contentType() hasContentType()
+*/
+void QHttpHeader::setContentType(const QString &type)
+{
+ setValue(QLatin1String("content-type"), type);
+}
+
+class QHttpResponseHeaderPrivate : public QHttpHeaderPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpResponseHeader)
+public:
+ int statCode;
+ QString reasonPhr;
+ int majVer;
+ int minVer;
+};
+
+/****************************************************
+ *
+ * QHttpResponseHeader
+ *
+ ****************************************************/
+
+/*!
+ \class QHttpResponseHeader
+ \obsolete
+ \brief The QHttpResponseHeader class contains response header information for HTTP.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ This class is used by the QHttp class to report the header
+ information that the client received from the server.
+
+ HTTP responses have a status code that indicates the status of the
+ response. This code is a 3-digit integer result code (for details
+ see to RFC 1945). In addition to the status code, you can also
+ specify a human-readable text that describes the reason for the
+ code ("reason phrase"). This class allows you to get the status
+ code and the reason phrase.
+
+ \sa QHttpRequestHeader, QHttp, {HTTP Example}
+*/
+
+/*!
+ Constructs an empty HTTP response header.
+*/
+QHttpResponseHeader::QHttpResponseHeader()
+ : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+ setValid(false);
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+QHttpResponseHeader::QHttpResponseHeader(const QHttpResponseHeader &header)
+ : QHttpHeader(*new QHttpResponseHeaderPrivate, header)
+{
+ Q_D(QHttpResponseHeader);
+ d->statCode = header.d_func()->statCode;
+ d->reasonPhr = header.d_func()->reasonPhr;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+}
+
+/*!
+ Copies the contents of \a header into this QHttpResponseHeader.
+*/
+QHttpResponseHeader &QHttpResponseHeader::operator=(const QHttpResponseHeader &header)
+{
+ Q_D(QHttpResponseHeader);
+ QHttpHeader::operator=(header);
+ d->statCode = header.d_func()->statCode;
+ d->reasonPhr = header.d_func()->reasonPhr;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+ return *this;
+}
+
+/*!
+ Constructs a HTTP response header from the string \a str. The
+ string is parsed and the information is set. The \a str should
+ consist of one or more "\r\n" delimited lines; the first line should be the
+ status-line (format: HTTP-version, space, status-code, space,
+ reason-phrase); each of remaining lines should have the format key, colon,
+ space, value.
+*/
+QHttpResponseHeader::QHttpResponseHeader(const QString &str)
+ : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+ parse(str);
+}
+
+/*!
+ \since 4.1
+
+ Constructs a QHttpResponseHeader, setting the status code to \a code, the
+ reason phrase to \a text and the protocol-version to \a majorVer and \a
+ minorVer.
+
+ \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+QHttpResponseHeader::QHttpResponseHeader(int code, const QString &text, int majorVer, int minorVer)
+ : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+ setStatusLine(code, text, majorVer, minorVer);
+}
+
+/*!
+ \since 4.1
+
+ Sets the status code to \a code, the reason phrase to \a text and
+ the protocol-version to \a majorVer and \a minorVer.
+
+ \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+void QHttpResponseHeader::setStatusLine(int code, const QString &text, int majorVer, int minorVer)
+{
+ Q_D(QHttpResponseHeader);
+ setValid(true);
+ d->statCode = code;
+ d->reasonPhr = text;
+ d->majVer = majorVer;
+ d->minVer = minorVer;
+}
+
+/*!
+ Returns the status code of the HTTP response header.
+
+ \sa reasonPhrase() majorVersion() minorVersion()
+*/
+int QHttpResponseHeader::statusCode() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->statCode;
+}
+
+/*!
+ Returns the reason phrase of the HTTP response header.
+
+ \sa statusCode() majorVersion() minorVersion()
+*/
+QString QHttpResponseHeader::reasonPhrase() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->reasonPhr;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP response header.
+
+ \sa minorVersion() statusCode() reasonPhrase()
+*/
+int QHttpResponseHeader::majorVersion() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP response header.
+
+ \sa majorVersion() statusCode() reasonPhrase()
+*/
+int QHttpResponseHeader::minorVersion() const
+{
+ Q_D(const QHttpResponseHeader);
+ return d->minVer;
+}
+
+/*! \internal
+*/
+bool QHttpResponseHeader::parseLine(const QString &line, int number)
+{
+ Q_D(QHttpResponseHeader);
+ if (number != 0)
+ return QHttpHeader::parseLine(line, number);
+
+ QString l = line.simplified();
+ if (l.length() < 10)
+ return false;
+
+ if (l.left(5) == QLatin1String("HTTP/") && l[5].isDigit() && l[6] == QLatin1Char('.') &&
+ l[7].isDigit() && l[8] == QLatin1Char(' ') && l[9].isDigit()) {
+ d->majVer = l[5].toLatin1() - '0';
+ d->minVer = l[7].toLatin1() - '0';
+
+ int pos = l.indexOf(QLatin1Char(' '), 9);
+ if (pos != -1) {
+ d->reasonPhr = l.mid(pos + 1);
+ d->statCode = l.mid(9, pos - 9).toInt();
+ } else {
+ d->statCode = l.mid(9).toInt();
+ d->reasonPhr.clear();
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+/*! \reimp
+*/
+QString QHttpResponseHeader::toString() const
+{
+ Q_D(const QHttpResponseHeader);
+ QString ret(QLatin1String("HTTP/%1.%2 %3 %4\r\n%5\r\n"));
+ return ret.arg(d->majVer).arg(d->minVer).arg(d->statCode).arg(d->reasonPhr).arg(QHttpHeader::toString());
+}
+
+class QHttpRequestHeaderPrivate : public QHttpHeaderPrivate
+{
+ Q_DECLARE_PUBLIC(QHttpRequestHeader)
+public:
+ QString m;
+ QString p;
+ int majVer;
+ int minVer;
+};
+
+/****************************************************
+ *
+ * QHttpRequestHeader
+ *
+ ****************************************************/
+
+/*!
+ \class QHttpRequestHeader
+ \obsolete
+ \brief The QHttpRequestHeader class contains request header information for HTTP.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ This class is used in the QHttp class to report the header
+ information if the client requests something from the server.
+
+ HTTP requests have a method which describes the request's action.
+ The most common requests are "GET" and "POST". In addition to the
+ request method the header also includes a request-URI to specify
+ the location for the method to use.
+
+ The method, request-URI and protocol-version can be set using a
+ constructor or later using setRequest(). The values can be
+ obtained using method(), path(), majorVersion() and
+ minorVersion().
+
+ Note that the request-URI must be in the format expected by the
+ HTTP server. That is, all reserved characters must be encoded in
+ %HH (where HH are two hexadecimal digits). See
+ QUrl::toPercentEncoding() for more information.
+
+ Important inherited functions: setValue() and value().
+
+ \sa QHttpResponseHeader QHttp
+*/
+
+/*!
+ Constructs an empty HTTP request header.
+*/
+QHttpRequestHeader::QHttpRequestHeader()
+ : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+ setValid(false);
+}
+
+/*!
+ Constructs a HTTP request header for the method \a method, the
+ request-URI \a path and the protocol-version \a majorVer and \a
+ minorVer. The \a path argument must be properly encoded for an
+ HTTP request.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QString &method, const QString &path, int majorVer, int minorVer)
+ : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+ Q_D(QHttpRequestHeader);
+ d->m = method;
+ d->p = path;
+ d->majVer = majorVer;
+ d->minVer = minorVer;
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QHttpRequestHeader &header)
+ : QHttpHeader(*new QHttpRequestHeaderPrivate, header)
+{
+ Q_D(QHttpRequestHeader);
+ d->m = header.d_func()->m;
+ d->p = header.d_func()->p;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+}
+
+/*!
+ Copies the content of \a header into this QHttpRequestHeader
+*/
+QHttpRequestHeader &QHttpRequestHeader::operator=(const QHttpRequestHeader &header)
+{
+ Q_D(QHttpRequestHeader);
+ QHttpHeader::operator=(header);
+ d->m = header.d_func()->m;
+ d->p = header.d_func()->p;
+ d->majVer = header.d_func()->majVer;
+ d->minVer = header.d_func()->minVer;
+ return *this;
+}
+
+/*!
+ Constructs a HTTP request header from the string \a str. The \a
+ str should consist of one or more "\r\n" delimited lines; the first line
+ should be the request-line (format: method, space, request-URI, space
+ HTTP-version); each of the remaining lines should have the format key,
+ colon, space, value.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QString &str)
+ : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+ parse(str);
+}
+
+/*!
+ This function sets the request method to \a method, the
+ request-URI to \a path and the protocol-version to \a majorVer and
+ \a minorVer. The \a path argument must be properly encoded for an
+ HTTP request.
+
+ \sa method() path() majorVersion() minorVersion()
+*/
+void QHttpRequestHeader::setRequest(const QString &method, const QString &path, int majorVer, int minorVer)
+{
+ Q_D(QHttpRequestHeader);
+ setValid(true);
+ d->m = method;
+ d->p = path;
+ d->majVer = majorVer;
+ d->minVer = minorVer;
+}
+
+/*!
+ Returns the method of the HTTP request header.
+
+ \sa path() majorVersion() minorVersion() setRequest()
+*/
+QString QHttpRequestHeader::method() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->m;
+}
+
+/*!
+ Returns the request-URI of the HTTP request header.
+
+ \sa method() majorVersion() minorVersion() setRequest()
+*/
+QString QHttpRequestHeader::path() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->p;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP request header.
+
+ \sa minorVersion() method() path() setRequest()
+*/
+int QHttpRequestHeader::majorVersion() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP request header.
+
+ \sa majorVersion() method() path() setRequest()
+*/
+int QHttpRequestHeader::minorVersion() const
+{
+ Q_D(const QHttpRequestHeader);
+ return d->minVer;
+}
+
+/*! \internal
+*/
+bool QHttpRequestHeader::parseLine(const QString &line, int number)
+{
+ Q_D(QHttpRequestHeader);
+ if (number != 0)
+ return QHttpHeader::parseLine(line, number);
+
+ QStringList lst = line.simplified().split(QLatin1String(" "));
+ if (lst.count() > 0) {
+ d->m = lst[0];
+ if (lst.count() > 1) {
+ d->p = lst[1];
+ if (lst.count() > 2) {
+ QString v = lst[2];
+ if (v.length() >= 8 && v.left(5) == QLatin1String("HTTP/") &&
+ v[5].isDigit() && v[6] == QLatin1Char('.') && v[7].isDigit()) {
+ d->majVer = v[5].toLatin1() - '0';
+ d->minVer = v[7].toLatin1() - '0';
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/*! \reimp
+*/
+QString QHttpRequestHeader::toString() const
+{
+ Q_D(const QHttpRequestHeader);
+ QString first(QLatin1String("%1 %2"));
+ QString last(QLatin1String(" HTTP/%3.%4\r\n%5\r\n"));
+ return first.arg(d->m).arg(d->p) +
+ last.arg(d->majVer).arg(d->minVer).arg(QHttpHeader::toString());
+}
+
+
+/****************************************************
+ *
+ * QHttp
+ *
+ ****************************************************/
+/*!
+ \class QHttp
+ \obsolete
+ \reentrant
+
+ \brief The QHttp class provides an implementation of the HTTP protocol.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+
+ This class provides a direct interface to HTTP that allows you to
+ download and upload data with the HTTP protocol.
+ However, for new applications, it is
+ recommended to use QNetworkAccessManager and QNetworkReply, as
+ those classes possess a simpler, yet more powerful API
+ and a more modern protocol implementation.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "requests"
+ in the rest of the documentation) are the following: setHost(),
+ get(), post(), head() and request().
+
+ All of these requests return a unique identifier that allows you
+ to keep track of the request that is currently executed. When the
+ execution of a request starts, the requestStarted() signal with
+ the identifier is emitted and when the request is finished, the
+ requestFinished() signal is emitted with the identifier and a bool
+ that indicates if the request finished with an error.
+
+ To make an HTTP request you must set up suitable HTTP headers. The
+ following example demonstrates how to request the main HTML page
+ from the Qt website (i.e., the URL \c http://qt.nokia.com/index.html):
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 2
+
+ For the common HTTP requests \c GET, \c POST and \c HEAD, QHttp
+ provides the convenience functions get(), post() and head(). They
+ already use a reasonable header and if you don't have to set
+ special header fields, they are easier to use. The above example
+ can also be written as:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 3
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 4
+
+ The dataSendProgress() and dataReadProgress() signals in the above
+ example are useful if you want to show a \link QProgressBar
+ progress bar\endlink to inform the user about the progress of the
+ download. The second argument is the total size of data. In
+ certain cases it is not possible to know the total amount in
+ advance, in which case the second argument is 0. (If you connect
+ to a QProgressBar a total of 0 results in a busy indicator.)
+
+ When the response header is read, it is reported with the
+ responseHeaderReceived() signal.
+
+ The readyRead() signal tells you that there is data ready to be
+ read. The amount of data can then be queried with the
+ bytesAvailable() function and it can be read with the read()
+ or readAll() functions.
+
+ If an error occurs during the execution of one of the commands in
+ a sequence of commands, all the pending commands (i.e. scheduled,
+ but not yet executed commands) are cleared and no signals are
+ emitted for them.
+
+ For example, if you have the following sequence of requests
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 5
+
+ and the get() request fails because the host lookup fails, then
+ the post() request is never executed and the signals would look
+ like this:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 6
+
+ You can then get details about the error with the error() and
+ errorString() functions. Note that only unexpected behavior, like
+ network failure is considered as an error. If the server response
+ contains an error status, like a 404 response, this is reported as
+ a normal response case. So you should always check the \link
+ QHttpResponseHeader::statusCode() status code \endlink of the
+ response header.
+
+ The functions currentId() and currentRequest() provide more
+ information about the currently executing request.
+
+ The functions hasPendingRequests() and clearPendingRequests()
+ allow you to query and clear the list of pending requests.
+
+ \sa QFtp, QNetworkAccessManager, QNetworkRequest, QNetworkReply,
+ {HTTP Example}, {Torrent Example}
+*/
+
+/*!
+ Constructs a QHttp object. The \a parent parameter is passed on
+ to the QObject constructor.
+*/
+QHttp::QHttp(QObject *parent)
+ : QObject(*new QHttpPrivate, parent)
+{
+ Q_D(QHttp);
+ d->init();
+}
+
+/*!
+ Constructs a QHttp object. Subsequent requests are done by
+ connecting to the server \a hostName on port \a port.
+
+ The \a parent parameter is passed on to the QObject constructor.
+
+ \sa setHost()
+*/
+QHttp::QHttp(const QString &hostName, quint16 port, QObject *parent)
+ : QObject(*new QHttpPrivate, parent)
+{
+ Q_D(QHttp);
+ d->init();
+
+ d->hostName = hostName;
+ d->port = port;
+}
+
+/*!
+ Constructs a QHttp object. Subsequent requests are done by
+ connecting to the server \a hostName on port \a port using the
+ connection mode \a mode.
+
+ If port is 0, it will use the default port for the \a mode used
+ (80 for Http and 443 for Https).
+
+ The \a parent parameter is passed on to the QObject constructor.
+
+ \sa setHost()
+*/
+QHttp::QHttp(const QString &hostName, ConnectionMode mode, quint16 port, QObject *parent)
+ : QObject(*new QHttpPrivate, parent)
+{
+ Q_D(QHttp);
+ d->init();
+
+ d->hostName = hostName;
+ if (port == 0)
+ port = (mode == ConnectionModeHttp) ? 80 : 443;
+ d->port = port;
+ d->mode = mode;
+}
+
+void QHttpPrivate::init()
+{
+ Q_Q(QHttp);
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown error"));
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+ post100ContinueTimer.setSingleShot(true);
+ QObject::connect(&post100ContinueTimer, SIGNAL(timeout()), q, SLOT(_q_continuePost()));
+}
+
+/*!
+ Destroys the QHttp object. If there is an open connection, it is
+ closed.
+*/
+QHttp::~QHttp()
+{
+ abort();
+}
+
+/*!
+ \enum QHttp::ConnectionMode
+ \since 4.3
+
+ This enum is used to specify the mode of connection to use:
+
+ \value ConnectionModeHttp The connection is a regular HTTP connection to the server
+ \value ConnectionModeHttps The HTTPS protocol is used and the connection is encrypted using SSL.
+
+ When using the HTTPS mode, care should be taken to connect to the sslErrors signal, and
+ handle possible SSL errors.
+
+ \sa QSslSocket
+*/
+
+/*!
+ \enum QHttp::State
+
+ This enum is used to specify the state the client is in:
+
+ \value Unconnected There is no connection to the host.
+ \value HostLookup A host name lookup is in progress.
+ \value Connecting An attempt to connect to the host is in progress.
+ \value Sending The client is sending its request to the server.
+ \value Reading The client's request has been sent and the client
+ is reading the server's response.
+ \value Connected The connection to the host is open, but the client is
+ neither sending a request, nor waiting for a response.
+ \value Closing The connection is closing down, but is not yet
+ closed. (The state will be \c Unconnected when the connection is
+ closed.)
+
+ \sa stateChanged() state()
+*/
+
+/*! \enum QHttp::Error
+
+ This enum identifies the error that occurred.
+
+ \value NoError No error occurred.
+ \value HostNotFound The host name lookup failed.
+ \value ConnectionRefused The server refused the connection.
+ \value UnexpectedClose The server closed the connection unexpectedly.
+ \value InvalidResponseHeader The server sent an invalid response header.
+ \value WrongContentLength The client could not read the content correctly
+ because an error with respect to the content length occurred.
+ \value Aborted The request was aborted with abort().
+ \value ProxyAuthenticationRequiredError QHttp is using a proxy, and the
+ proxy server requires authentication to establish a connection.
+ \value AuthenticationRequiredError The web server requires authentication
+ to complete the request.
+ \value UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \fn void QHttp::stateChanged(int state)
+
+ This signal is emitted when the state of the QHttp object changes.
+ The argument \a state is the new state of the connection; it is
+ one of the \l State values.
+
+ This usually happens when a request is started, but it can also
+ happen when the server closes the connection or when a call to
+ close() succeeded.
+
+ \sa get() post() head() request() close() state() State
+*/
+
+/*!
+ \fn void QHttp::responseHeaderReceived(const QHttpResponseHeader &resp);
+
+ This signal is emitted when the HTTP header of a server response
+ is available. The header is passed in \a resp.
+
+ \sa get() post() head() request() readyRead()
+*/
+
+/*!
+ \fn void QHttp::readyRead(const QHttpResponseHeader &resp)
+
+ This signal is emitted when there is new response data to read.
+
+ If you specified a device in the request where the data should be
+ written to, then this signal is \e not emitted; instead the data
+ is written directly to the device.
+
+ The response header is passed in \a resp.
+
+ You can read the data with the readAll() or read() functions
+
+ This signal is useful if you want to process the data in chunks as
+ soon as it becomes available. If you are only interested in the
+ complete data, just connect to the requestFinished() signal and
+ read the data then instead.
+
+ \sa get() post() request() readAll() read() bytesAvailable()
+*/
+
+/*!
+ \fn void QHttp::dataSendProgress(int done, int total)
+
+ This signal is emitted when this object sends data to a HTTP
+ server to inform it about the progress of the upload.
+
+ \a done is the amount of data that has already arrived and \a
+ total is the total amount of data. It is possible that the total
+ amount of data that should be transferred cannot be determined, in
+ which case \a total is 0.(If you connect to a QProgressBar, the
+ progress bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa dataReadProgress(), post(), request(), QProgressBar
+*/
+
+/*!
+ \fn void QHttp::dataReadProgress(int done, int total)
+
+ This signal is emitted when this object reads data from a HTTP
+ server to indicate the current progress of the download.
+
+ \a done is the amount of data that has already arrived and \a
+ total is the total amount of data. It is possible that the total
+ amount of data that should be transferred cannot be determined, in
+ which case \a total is 0.(If you connect to a QProgressBar, the
+ progress bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa dataSendProgress() get() post() request() QProgressBar
+*/
+
+/*!
+ \fn void QHttp::requestStarted(int id)
+
+ This signal is emitted when processing the request identified by
+ \a id starts.
+
+ \sa requestFinished() done()
+*/
+
+/*!
+ \fn void QHttp::requestFinished(int id, bool error)
+
+ This signal is emitted when processing the request identified by
+ \a id has finished. \a error is true if an error occurred during
+ the processing; otherwise \a error is false.
+
+ \sa requestStarted() done() error() errorString()
+*/
+
+/*!
+ \fn void QHttp::done(bool error)
+
+ This signal is emitted when the last pending request has finished;
+ (it is emitted after the last request's requestFinished() signal).
+ \a error is true if an error occurred during the processing;
+ otherwise \a error is false.
+
+ \sa requestFinished() error() errorString()
+*/
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*!
+ \fn void QHttp::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
+ \since 4.3
+
+ This signal can be emitted when a \a proxy that requires
+ authentication is used. The \a authenticator object can then be
+ filled in with the required details to allow authentication and
+ continue the connection.
+
+ \note It is not possible to use a QueuedConnection to connect to
+ this signal, as the connection will fail if the authenticator has
+ not been filled in with new information when the signal returns.
+
+ \sa QAuthenticator, QNetworkProxy
+*/
+
+#endif
+
+/*!
+ \fn void QHttp::authenticationRequired(const QString &hostname, quint16 port, QAuthenticator *authenticator)
+ \since 4.3
+
+ This signal can be emitted when a web server on a given \a hostname and \a
+ port requires authentication. The \a authenticator object can then be
+ filled in with the required details to allow authentication and continue
+ the connection.
+
+ \note It is not possible to use a QueuedConnection to connect to
+ this signal, as the connection will fail if the authenticator has
+ not been filled in with new information when the signal returns.
+
+ \sa QAuthenticator, QNetworkProxy
+*/
+
+/*!
+ \fn void QHttp::sslErrors(const QList<QSslError> &errors)
+ \since 4.3
+
+ Forwards the sslErrors signal from the QSslSocket used in QHttp. \a errors
+ is the list of errors that occurred during the SSL handshake. Unless you
+ call ignoreSslErrors() from within a slot connected to this signal when an
+ error occurs, QHttp will tear down the connection immediately after
+ emitting the signal.
+
+ \sa QSslSocket QSslSocket::ignoreSslErrors()
+*/
+
+/*!
+ Aborts the current request and deletes all scheduled requests.
+
+ For the current request, the requestFinished() signal with the \c
+ error argument \c true is emitted. For all other requests that are
+ affected by the abort(), no signals are emitted.
+
+ Since this slot also deletes the scheduled requests, there are no
+ requests left and the done() signal is emitted (with the \c error
+ argument \c true).
+
+ \sa clearPendingRequests()
+*/
+void QHttp::abort()
+{
+ Q_D(QHttp);
+ if (d->pending.isEmpty())
+ return;
+
+ d->finishedWithError(tr("Request aborted"), Aborted);
+ clearPendingRequests();
+ if (d->socket)
+ d->socket->abort();
+ d->closeConn();
+}
+
+/*!
+ Returns the number of bytes that can be read from the response
+ content at the moment.
+
+ \sa get() post() request() readyRead() read() readAll()
+*/
+qint64 QHttp::bytesAvailable() const
+{
+ Q_D(const QHttp);
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::bytesAvailable(): %d bytes", (int)d->rba.size());
+#endif
+ return qint64(d->rba.size());
+}
+
+/*! \fn qint64 QHttp::readBlock(char *data, quint64 maxlen)
+
+ Use read() instead.
+*/
+
+/*!
+ Reads \a maxlen bytes from the response content into \a data and
+ returns the number of bytes read. Returns -1 if an error occurred.
+
+ \sa get() post() request() readyRead() bytesAvailable() readAll()
+*/
+qint64 QHttp::read(char *data, qint64 maxlen)
+{
+ Q_D(QHttp);
+ if (data == 0 && maxlen != 0) {
+ qWarning("QHttp::read: Null pointer error");
+ return -1;
+ }
+ if (maxlen >= d->rba.size())
+ maxlen = d->rba.size();
+ int readSoFar = 0;
+ while (!d->rba.isEmpty() && readSoFar < maxlen) {
+ int nextBlockSize = d->rba.nextDataBlockSize();
+ int bytesToRead = qMin<qint64>(maxlen - readSoFar, nextBlockSize);
+ memcpy(data + readSoFar, d->rba.readPointer(), bytesToRead);
+ d->rba.free(bytesToRead);
+ readSoFar += bytesToRead;
+ }
+
+ d->bytesDone += maxlen;
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::read(): read %lld bytes (%lld bytes done)", maxlen, d->bytesDone);
+#endif
+ return maxlen;
+}
+
+/*!
+ Reads all the bytes from the response content and returns them.
+
+ \sa get() post() request() readyRead() bytesAvailable() read()
+*/
+QByteArray QHttp::readAll()
+{
+ qint64 avail = bytesAvailable();
+ QByteArray tmp;
+ tmp.resize(int(avail));
+ qint64 got = read(tmp.data(), int(avail));
+ tmp.resize(got);
+ return tmp;
+}
+
+/*!
+ Returns the identifier of the HTTP request being executed or 0 if
+ there is no request being executed (i.e. they've all finished).
+
+ \sa currentRequest()
+*/
+int QHttp::currentId() const
+{
+ Q_D(const QHttp);
+ if (d->pending.isEmpty())
+ return 0;
+ return d->pending.first()->id;
+}
+
+/*!
+ Returns the request header of the HTTP request being executed. If
+ the request is one issued by setHost() or close(), it
+ returns an invalid request header, i.e.
+ QHttpRequestHeader::isValid() returns false.
+
+ \sa currentId()
+*/
+QHttpRequestHeader QHttp::currentRequest() const
+{
+ Q_D(const QHttp);
+ if (!d->pending.isEmpty()) {
+ QHttpRequest *r = d->pending.first();
+ if (r->hasRequestHeader())
+ return r->requestHeader();
+ }
+ return QHttpRequestHeader();
+}
+
+/*!
+ Returns the received response header of the most recently finished HTTP
+ request. If no response has yet been received
+ QHttpResponseHeader::isValid() will return false.
+
+ \sa currentRequest()
+*/
+QHttpResponseHeader QHttp::lastResponse() const
+{
+ Q_D(const QHttp);
+ return d->response;
+}
+
+/*!
+ Returns the QIODevice pointer that is used as the data source of the HTTP
+ request being executed. If there is no current request or if the request
+ does not use an IO device as the data source, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentDestinationDevice() post() request()
+*/
+QIODevice *QHttp::currentSourceDevice() const
+{
+ Q_D(const QHttp);
+ if (d->pending.isEmpty())
+ return 0;
+ return d->pending.first()->sourceDevice();
+}
+
+/*!
+ Returns the QIODevice pointer that is used as to store the data of the HTTP
+ request being executed. If there is no current request or if the request
+ does not store the data to an IO device, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentSourceDevice() get() post() request()
+*/
+QIODevice *QHttp::currentDestinationDevice() const
+{
+ Q_D(const QHttp);
+ if (d->pending.isEmpty())
+ return 0;
+ return d->pending.first()->destinationDevice();
+}
+
+/*!
+ Returns true if there are any requests scheduled that have not yet
+ been executed; otherwise returns false.
+
+ The request that is being executed is \e not considered as a
+ scheduled request.
+
+ \sa clearPendingRequests() currentId() currentRequest()
+*/
+bool QHttp::hasPendingRequests() const
+{
+ Q_D(const QHttp);
+ return d->pending.count() > 1;
+}
+
+/*!
+ Deletes all pending requests from the list of scheduled requests.
+ This does not affect the request that is being executed. If
+ you want to stop this as well, use abort().
+
+ \sa hasPendingRequests() abort()
+*/
+void QHttp::clearPendingRequests()
+{
+ Q_D(QHttp);
+ // delete all entires except the first one
+ while (d->pending.count() > 1)
+ delete d->pending.takeLast();
+}
+
+/*!
+ Sets the HTTP server that is used for requests to \a hostName on
+ port \a port.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::setHost(const QString &hostName, quint16 port)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetHostRequest(hostName, port, ConnectionModeHttp));
+}
+
+/*!
+ Sets the HTTP server that is used for requests to \a hostName on
+ port \a port using the connection mode \a mode.
+
+ If port is 0, it will use the default port for the \a mode used
+ (80 for HTTP and 443 for HTTPS).
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::setHost(const QString &hostName, ConnectionMode mode, quint16 port)
+{
+#ifdef QT_NO_OPENSSL
+ if (mode == ConnectionModeHttps)
+ qWarning("QHttp::setHost: HTTPS connection requested but SSL support not compiled in");
+#endif
+ Q_D(QHttp);
+ if (port == 0)
+ port = (mode == ConnectionModeHttp) ? 80 : 443;
+ return d->addRequest(new QHttpSetHostRequest(hostName, port, mode));
+}
+
+/*!
+ Replaces the internal QTcpSocket that QHttp uses with \a
+ socket. This is useful if you want to use your own custom QTcpSocket
+ subclass instead of the plain QTcpSocket that QHttp uses by default.
+ QHttp does not take ownership of the socket, and will not delete \a
+ socket when destroyed.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ Note: If QHttp is used in a non-GUI thread that runs its own event
+ loop, you must move \a socket to that thread before calling setSocket().
+
+ \sa QObject::moveToThread(), {Thread Support in Qt}
+*/
+int QHttp::setSocket(QTcpSocket *socket)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetSocketRequest(socket));
+}
+
+/*!
+ This function sets the user name \a userName and password \a
+ password for web pages that require authentication.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+*/
+int QHttp::setUser(const QString &userName, const QString &password)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetUserRequest(userName, password));
+}
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*!
+ Enables HTTP proxy support, using the proxy server \a host on port \a
+ port. \a username and \a password can be provided if the proxy server
+ requires authentication.
+
+ Example:
+
+ \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 7
+
+ QHttp supports non-transparent web proxy servers only, such as the Squid
+ Web proxy cache server (from \l http://www.squid.org/). For transparent
+ proxying, such as SOCKS5, use QNetworkProxy instead.
+
+ \note setProxy() has to be called before setHost() for it to take effect.
+ If setProxy() is called after setHost(), then it will not apply until after
+ setHost() is called again.
+
+ \sa QFtp::setProxy()
+*/
+int QHttp::setProxy(const QString &host, int port,
+ const QString &username, const QString &password)
+{
+ Q_D(QHttp);
+ QNetworkProxy proxy(QNetworkProxy::HttpProxy, host, port, username, password);
+ return d->addRequest(new QHttpSetProxyRequest(proxy));
+}
+
+/*!
+ \overload
+
+ Enables HTTP proxy support using the proxy settings from \a
+ proxy. If \a proxy is a transparent proxy, QHttp will call
+ QAbstractSocket::setProxy() on the underlying socket. If the type
+ is QNetworkProxy::HttpCachingProxy, QHttp will behave like the
+ previous function.
+
+ \note for compatibility with Qt 4.3, if the proxy type is
+ QNetworkProxy::HttpProxy and the request type is unencrypted (that
+ is, ConnectionModeHttp), QHttp will treat the proxy as a caching
+ proxy.
+*/
+int QHttp::setProxy(const QNetworkProxy &proxy)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpSetProxyRequest(proxy));
+}
+
+#endif
+
+/*!
+ Sends a get request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be a absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html and
+ must be encoded with either QUrl::toPercentEncoding() or
+ QUrl::encodedPath().
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ \section1 Request Processing
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost(), post(), head(), request(), requestStarted(),
+ requestFinished(), done()
+*/
+int QHttp::get(const QString &path, QIODevice *to)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("GET"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, (QIODevice *) 0, to));
+}
+
+/*!
+ Sends a post request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html and
+ must be encoded with either QUrl::toPercentEncoding() or
+ QUrl::encodedPath().
+
+ The incoming data comes via the \a data IO device.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::post(const QString &path, QIODevice *data, QIODevice *to )
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("POST"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, data, to));
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int QHttp::post(const QString &path, const QByteArray &data, QIODevice *to)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("POST"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, new QByteArray(data), to));
+}
+
+/*!
+ Sends a header request for \a path to the server set by setHost()
+ or as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://example.com/index.html.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() post() request() requestStarted() requestFinished() done()
+*/
+int QHttp::head(const QString &path)
+{
+ Q_D(QHttp);
+ QHttpRequestHeader header(QLatin1String("HEAD"), path);
+ header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+ return d->addRequest(new QHttpPGHRequest(header, (QIODevice*)0, 0));
+}
+
+/*!
+ Sends a request to the server set by setHost() or as specified in
+ the constructor. Uses the \a header as the HTTP request header.
+ You are responsible for setting up a header that is appropriate
+ for your request.
+
+ The incoming data comes via the \a data IO device.
+
+ If the IO device \a to is 0 the readyRead() signal is emitted
+ every time new content data is available to read.
+
+ If the IO device \a to is not 0, the content data of the response
+ is written directly to the device. Make sure that the \a to
+ pointer is valid for the duration of the operation (it is safe to
+ delete it when the requestFinished() signal is emitted).
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ \sa setHost() get() post() head() requestStarted() requestFinished() done()
+*/
+int QHttp::request(const QHttpRequestHeader &header, QIODevice *data, QIODevice *to)
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpNormalRequest(header, data, to));
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int QHttp::request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to )
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpNormalRequest(header, new QByteArray(data), to));
+}
+
+/*!
+ Closes the connection; this is useful if you have a keep-alive
+ connection and want to close it.
+
+ For the requests issued with get(), post() and head(), QHttp sets
+ the connection to be keep-alive. You can also do this using the
+ header you pass to the request() function. QHttp only closes the
+ connection to the HTTP server if the response header requires it
+ to do so.
+
+ The function does not block; instead, it returns immediately. The request
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ requestStarted() and requestFinished().
+
+ When the request is started the requestStarted() signal is
+ emitted. When it is finished the requestFinished() signal is
+ emitted.
+
+ If you want to close the connection immediately, you have to use
+ abort() instead.
+
+ \sa stateChanged() abort() requestStarted() requestFinished() done()
+*/
+int QHttp::close()
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpCloseRequest());
+}
+
+/*!
+ \obsolete
+
+ Behaves the same as close().
+*/
+int QHttp::closeConnection()
+{
+ Q_D(QHttp);
+ return d->addRequest(new QHttpCloseRequest());
+}
+
+int QHttpPrivate::addRequest(QHttpNormalRequest *req)
+{
+ QHttpRequestHeader h = req->requestHeader();
+ if (h.path().isEmpty()) {
+ // note: the following qWarning is autotested. If you change it, change the test too.
+ qWarning("QHttp: empty path requested is invalid -- using '/'");
+ h.setRequest(h.method(), QLatin1String("/"), h.majorVersion(), h.minorVersion());
+ req->setRequestHeader(h);
+ }
+
+ // contine below
+ return addRequest(static_cast<QHttpRequest *>(req));
+}
+
+int QHttpPrivate::addRequest(QHttpRequest *req)
+{
+ Q_Q(QHttp);
+ pending.append(req);
+
+ if (pending.count() == 1) {
+ // don't emit the requestStarted() signal before the id is returned
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ }
+ return req->id;
+}
+
+void QHttpPrivate::_q_startNextRequest()
+{
+ Q_Q(QHttp);
+ if (pending.isEmpty())
+ return;
+ QHttpRequest *r = pending.first();
+
+ error = QHttp::NoError;
+ errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown error"));
+
+ if (q->bytesAvailable() != 0)
+ q->readAll(); // clear the data
+ emit q->requestStarted(r->id);
+ r->start(q);
+}
+
+void QHttpPrivate::_q_slotSendRequest()
+{
+ if (hostName.isNull()) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "No server set to connect to")),
+ QHttp::UnknownError);
+ return;
+ }
+
+ QString connectionHost = hostName;
+ int connectionPort = port;
+ bool sslInUse = false;
+
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
+ if (mode == QHttp::ConnectionModeHttps || (sslSocket && sslSocket->isEncrypted()))
+ sslInUse = true;
+#endif
+
+#ifndef QT_NO_NETWORKPROXY
+ bool cachingProxyInUse = false;
+ bool transparentProxyInUse = false;
+ if (proxy.type() == QNetworkProxy::DefaultProxy)
+ proxy = QNetworkProxy::applicationProxy();
+
+ if (proxy.type() == QNetworkProxy::HttpCachingProxy) {
+ if (proxy.hostName().isEmpty())
+ proxy.setType(QNetworkProxy::NoProxy);
+ else
+ cachingProxyInUse = true;
+ } else if (proxy.type() == QNetworkProxy::HttpProxy) {
+ // Compatibility behaviour: HttpProxy can be used to mean both
+ // transparent and caching proxy
+ if (proxy.hostName().isEmpty()) {
+ proxy.setType(QNetworkProxy::NoProxy);
+ } else if (sslInUse) {
+ // Disallow use of caching proxy with HTTPS; instead fall back to
+ // transparent HTTP CONNECT proxying.
+ transparentProxyInUse = true;
+ } else {
+ proxy.setType(QNetworkProxy::HttpCachingProxy);
+ cachingProxyInUse = true;
+ }
+ }
+
+ // Proxy support. Insert the Proxy-Authorization item into the
+ // header before it's sent off to the proxy.
+ if (cachingProxyInUse) {
+ QUrl proxyUrl;
+ proxyUrl.setScheme(QLatin1String("http"));
+ proxyUrl.setHost(hostName);
+ if (port && port != 80)
+ proxyUrl.setPort(port);
+ QString request = QString::fromAscii(proxyUrl.resolved(QUrl::fromEncoded(header.path().toLatin1())).toEncoded());
+
+ header.setRequest(header.method(), request, header.majorVersion(), header.minorVersion());
+ header.setValue(QLatin1String("Proxy-Connection"), QLatin1String("keep-alive"));
+
+ QAuthenticatorPrivate *auth = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
+ if (auth && auth->method != QAuthenticatorPrivate::None) {
+ QByteArray response = auth->calculateResponse(header.method().toLatin1(), header.path().toLatin1());
+ header.setValue(QLatin1String("Proxy-Authorization"), QString::fromLatin1(response));
+ }
+
+ connectionHost = proxy.hostName();
+ connectionPort = proxy.port();
+ }
+
+ if (transparentProxyInUse || sslInUse) {
+ socket->setProxy(proxy);
+ }
+#endif
+
+ // Username support. Insert the user and password into the query
+ // string.
+ QAuthenticatorPrivate *auth = QAuthenticatorPrivate::getPrivate(authenticator);
+ if (auth && auth->method != QAuthenticatorPrivate::None) {
+ QByteArray response = auth->calculateResponse(header.method().toLatin1(), header.path().toLatin1());
+ header.setValue(QLatin1String("Authorization"), QString::fromLatin1(response));
+ }
+
+ // Do we need to setup a new connection or can we reuse an
+ // existing one?
+ if (socket->peerName() != connectionHost || socket->peerPort() != connectionPort
+ || socket->state() != QTcpSocket::ConnectedState
+#ifndef QT_NO_OPENSSL
+ || (sslSocket && sslSocket->isEncrypted() != (mode == QHttp::ConnectionModeHttps))
+#endif
+ ) {
+ socket->blockSignals(true);
+ socket->abort();
+ socket->blockSignals(false);
+
+ setState(QHttp::Connecting);
+#ifndef QT_NO_OPENSSL
+ if (sslSocket && mode == QHttp::ConnectionModeHttps) {
+ sslSocket->connectToHostEncrypted(hostName, port);
+ } else
+#endif
+ {
+ socket->connectToHost(connectionHost, connectionPort);
+ }
+ } else {
+ _q_slotConnected();
+ }
+
+}
+
+void QHttpPrivate::finishedWithSuccess()
+{
+ Q_Q(QHttp);
+ if (pending.isEmpty())
+ return;
+ QHttpRequest *r = pending.first();
+
+ // did we recurse?
+ if (r->finished)
+ return;
+ r->finished = true;
+ hasFinishedWithError = false;
+
+ emit q->requestFinished(r->id, false);
+ if (hasFinishedWithError) {
+ // we recursed and changed into an error. The finishedWithError function
+ // below has emitted the done(bool) signal and cleared the queue by now.
+ return;
+ }
+
+ pending.removeFirst();
+ delete r;
+
+ if (pending.isEmpty()) {
+ emit q->done(false);
+ } else {
+ _q_startNextRequest();
+ }
+}
+
+void QHttpPrivate::finishedWithError(const QString &detail, int errorCode)
+{
+ Q_Q(QHttp);
+ if (pending.isEmpty())
+ return;
+ QHttpRequest *r = pending.first();
+ hasFinishedWithError = true;
+
+ error = QHttp::Error(errorCode);
+ errorString = detail;
+
+ // did we recurse?
+ if (!r->finished) {
+ r->finished = true;
+ emit q->requestFinished(r->id, true);
+ }
+
+ while (!pending.isEmpty())
+ delete pending.takeFirst();
+ emit q->done(hasFinishedWithError);
+}
+
+void QHttpPrivate::_q_slotClosed()
+{
+ Q_Q(QHttp);
+
+ if (state == QHttp::Reading) {
+ if (response.hasKey(QLatin1String("content-length"))) {
+ // We got Content-Length, so did we get all bytes?
+ if (bytesDone + q->bytesAvailable() != response.contentLength()) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Wrong content length")), QHttp::WrongContentLength);
+ }
+ }
+ } else if (state == QHttp::Connecting || state == QHttp::Sending) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Server closed connection unexpectedly")), QHttp::UnexpectedClose);
+ }
+
+ postDevice = 0;
+ if (state != QHttp::Closing)
+ setState(QHttp::Closing);
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+}
+
+void QHttpPrivate::_q_continuePost()
+{
+ if (pendingPost) {
+ pendingPost = false;
+ setState(QHttp::Sending);
+ _q_slotBytesWritten(0);
+ }
+}
+
+void QHttpPrivate::_q_slotConnected()
+{
+ if (state != QHttp::Sending) {
+ bytesDone = 0;
+ setState(QHttp::Sending);
+ }
+
+ QString str = header.toString();
+ bytesTotal = str.length();
+ socket->write(str.toLatin1(), bytesTotal);
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp: write request header %p:\n---{\n%s}---", &header, str.toLatin1().constData());
+#endif
+
+ if (postDevice) {
+ postDevice->seek(0); // reposition the device
+ bytesTotal += postDevice->size();
+ //check for 100-continue
+ if (header.value(QLatin1String("expect")).contains(QLatin1String("100-continue"), Qt::CaseInsensitive)) {
+ //create a time out for 2 secs.
+ pendingPost = true;
+ post100ContinueTimer.start(2000);
+ }
+ } else {
+ bytesTotal += buffer.size();
+ socket->write(buffer, buffer.size());
+ }
+}
+
+void QHttpPrivate::_q_slotError(QAbstractSocket::SocketError err)
+{
+ Q_Q(QHttp);
+ postDevice = 0;
+
+ if (state == QHttp::Connecting || state == QHttp::Reading || state == QHttp::Sending) {
+ switch (err) {
+ case QTcpSocket::ConnectionRefusedError:
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused (or timed out)")), QHttp::ConnectionRefused);
+ break;
+ case QTcpSocket::HostNotFoundError:
+ finishedWithError(QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
+ .arg(socket->peerName()), QHttp::HostNotFound);
+ break;
+ case QTcpSocket::RemoteHostClosedError:
+ if (state == QHttp::Sending && reconnectAttempts--) {
+ setState(QHttp::Closing);
+ setState(QHttp::Unconnected);
+ socket->blockSignals(true);
+ socket->abort();
+ socket->blockSignals(false);
+ QMetaObject::invokeMethod(q, "_q_slotSendRequest", Qt::QueuedConnection);
+ return;
+ }
+ break;
+#ifndef QT_NO_NETWORKPROXY
+ case QTcpSocket::ProxyAuthenticationRequiredError:
+ finishedWithError(socket->errorString(), QHttp::ProxyAuthenticationRequiredError);
+ break;
+#endif
+ default:
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTP request failed")), QHttp::UnknownError);
+ break;
+ }
+ }
+
+ closeConn();
+}
+
+#ifndef QT_NO_OPENSSL
+void QHttpPrivate::_q_slotEncryptedBytesWritten(qint64 written)
+{
+ Q_UNUSED(written);
+ postMoreData();
+}
+#endif
+
+void QHttpPrivate::_q_slotBytesWritten(qint64 written)
+{
+ Q_Q(QHttp);
+ bytesDone += written;
+ emit q->dataSendProgress(bytesDone, bytesTotal);
+ postMoreData();
+}
+
+// Send the POST data
+void QHttpPrivate::postMoreData()
+{
+ if (pendingPost)
+ return;
+
+ if (!postDevice)
+ return;
+
+ // the following is backported code from Qt 4.6 QNetworkAccessManager.
+ // We also have to check the encryptedBytesToWrite() if it is an SSL socket.
+#ifndef QT_NO_OPENSSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+ // if it is really an ssl socket, check more than just bytesToWrite()
+ if ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) == 0) {
+#else
+ if (socket->bytesToWrite() == 0) {
+#endif
+ int max = qMin<qint64>(4096, postDevice->size() - postDevice->pos());
+ QByteArray arr;
+ arr.resize(max);
+
+ int n = postDevice->read(arr.data(), max);
+ if (n < 0) {
+ qWarning("Could not read enough bytes from the device");
+ closeConn();
+ return;
+ }
+ if (postDevice->atEnd()) {
+ postDevice = 0;
+ }
+
+ socket->write(arr, n);
+ }
+}
+
+void QHttpPrivate::_q_slotReadyRead()
+{
+ Q_Q(QHttp);
+ QHttp::State oldState = state;
+ if (state != QHttp::Reading) {
+ setState(QHttp::Reading);
+ readHeader = true;
+ headerStr = QLatin1String("");
+ bytesDone = 0;
+ chunkedSize = -1;
+ repost = false;
+ }
+
+ while (readHeader) {
+ bool end = false;
+ QString tmp;
+ while (!end && socket->canReadLine()) {
+ tmp = QString::fromAscii(socket->readLine());
+ if (tmp == QLatin1String("\r\n") || tmp == QLatin1String("\n") || tmp.isEmpty())
+ end = true;
+ else
+ headerStr += tmp;
+ }
+
+ if (!end)
+ return;
+
+ response = QHttpResponseHeader(headerStr);
+ headerStr = QLatin1String("");
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp: read response header:\n---{\n%s}---", response.toString().toLatin1().constData());
+#endif
+ // Check header
+ if (!response.isValid()) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP response header")),
+ QHttp::InvalidResponseHeader);
+ closeConn();
+ return;
+ }
+
+ int statusCode = response.statusCode();
+ if (statusCode == 401 || statusCode == 407) { // (Proxy) Authentication required
+ QAuthenticator *auth =
+#ifndef QT_NO_NETWORKPROXY
+ statusCode == 407
+ ? &proxyAuthenticator :
+#endif
+ &authenticator;
+ if (auth->isNull())
+ auth->detach();
+ QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
+ priv->parseHttpResponse(response, (statusCode == 407));
+ if (priv->phase == QAuthenticatorPrivate::Done) {
+ socket->blockSignals(true);
+#ifndef QT_NO_NETWORKPROXY
+ if (statusCode == 407)
+ emit q->proxyAuthenticationRequired(proxy, auth);
+ else
+#endif
+ emit q->authenticationRequired(hostName, port, auth);
+ socket->blockSignals(false);
+ } else if (priv->phase == QAuthenticatorPrivate::Invalid) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown authentication method")),
+ QHttp::AuthenticationRequiredError);
+ closeConn();
+ return;
+ }
+
+ // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
+ if (priv->phase == QAuthenticatorPrivate::Done) {
+#ifndef QT_NO_NETWORKPROXY
+ if (statusCode == 407)
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy authentication required")),
+ QHttp::ProxyAuthenticationRequiredError);
+ else
+#endif
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Authentication required")),
+ QHttp::AuthenticationRequiredError);
+ closeConn();
+ return;
+ } else {
+ // close the connection if it isn't already and reconnect using the chosen authentication method
+ bool willClose = (response.value(QLatin1String("proxy-connection")).toLower() == QLatin1String("close"))
+ || (response.value(QLatin1String("connection")).toLower() == QLatin1String("close"));
+ if (willClose) {
+ if (socket) {
+ setState(QHttp::Closing);
+ socket->blockSignals(true);
+ socket->close();
+ socket->blockSignals(false);
+ socket->readAll();
+ }
+ _q_slotSendRequest();
+ return;
+ } else {
+ repost = true;
+ }
+ }
+ } else {
+ buffer.clear();
+ }
+
+ if (response.statusCode() == 100 && pendingPost) {
+ // if we have pending POST, start sending data otherwise ignore
+ post100ContinueTimer.stop();
+ QMetaObject::invokeMethod(q, "_q_continuePost", Qt::QueuedConnection);
+ return;
+ }
+
+ // The 100-continue header is ignored (in case of no 'expect:100-continue' header),
+ // because when using the POST method, we send both the request header and data in
+ // one chunk.
+ if (response.statusCode() != 100) {
+ post100ContinueTimer.stop();
+ pendingPost = false;
+ readHeader = false;
+ if (response.hasKey(QLatin1String("transfer-encoding")) &&
+ response.value(QLatin1String("transfer-encoding")).toLower().contains(QLatin1String("chunked")))
+ chunkedSize = 0;
+
+ if (!repost)
+ emit q->responseHeaderReceived(response);
+ if (state == QHttp::Unconnected || state == QHttp::Closing)
+ return;
+ } else {
+ // Restore the state, the next incoming data will be treated as if
+ // we never say the 100 response.
+ state = oldState;
+ }
+ }
+
+ bool everythingRead = false;
+
+ if (q->currentRequest().method() == QLatin1String("HEAD") ||
+ response.statusCode() == 304 || response.statusCode() == 204 ||
+ response.statusCode() == 205) {
+ // HEAD requests have only headers as replies
+ // These status codes never have a body:
+ // 304 Not Modified
+ // 204 No Content
+ // 205 Reset Content
+ everythingRead = true;
+ } else {
+ qint64 n = socket->bytesAvailable();
+ QByteArray *arr = 0;
+ if (chunkedSize != -1) {
+ // transfer-encoding is chunked
+ for (;;) {
+ // get chunk size
+ if (chunkedSize == 0) {
+ if (!socket->canReadLine())
+ break;
+ QString sizeString = QString::fromAscii(socket->readLine());
+ int tPos = sizeString.indexOf(QLatin1Char(';'));
+ if (tPos != -1)
+ sizeString.truncate(tPos);
+ bool ok;
+ chunkedSize = sizeString.toInt(&ok, 16);
+ if (!ok) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP chunked body")),
+ QHttp::WrongContentLength);
+ closeConn();
+ delete arr;
+ return;
+ }
+ if (chunkedSize == 0) // last-chunk
+ chunkedSize = -2;
+ }
+
+ // read trailer
+ while (chunkedSize == -2 && socket->canReadLine()) {
+ QString read = QString::fromAscii(socket->readLine());
+ if (read == QLatin1String("\r\n") || read == QLatin1String("\n"))
+ chunkedSize = -1;
+ }
+ if (chunkedSize == -1) {
+ everythingRead = true;
+ break;
+ }
+
+ // make sure that you can read the terminating CRLF,
+ // otherwise wait until next time...
+ n = socket->bytesAvailable();
+ if (n == 0)
+ break;
+ if (n == chunkedSize || n == chunkedSize+1) {
+ n = chunkedSize - 1;
+ if (n == 0)
+ break;
+ }
+
+ // read data
+ qint64 toRead = chunkedSize < 0 ? n : qMin(n, chunkedSize);
+ if (!arr)
+ arr = new QByteArray;
+ uint oldArrSize = arr->size();
+ arr->resize(oldArrSize + toRead);
+ qint64 read = socket->read(arr->data()+oldArrSize, toRead);
+ arr->resize(oldArrSize + read);
+
+ chunkedSize -= read;
+
+ if (chunkedSize == 0 && n - read >= 2) {
+ // read terminating CRLF
+ char tmp[2];
+ socket->read(tmp, 2);
+ if (tmp[0] != '\r' || tmp[1] != '\n') {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP chunked body")),
+ QHttp::WrongContentLength);
+ closeConn();
+ delete arr;
+ return;
+ }
+ }
+ }
+ } else if (response.hasContentLength()) {
+ if (repost && (n < response.contentLength())) {
+ // wait for the content to be available fully
+ // if repost is required, the content is ignored
+ return;
+ }
+ n = qMin(qint64(response.contentLength() - bytesDone), n);
+ if (n > 0) {
+ arr = new QByteArray;
+ arr->resize(n);
+ qint64 read = socket->read(arr->data(), n);
+ arr->resize(read);
+ }
+ if (bytesDone + q->bytesAvailable() + n == response.contentLength())
+ everythingRead = true;
+ } else if (n > 0) {
+ // workaround for VC++ bug
+ QByteArray temp = socket->readAll();
+ arr = new QByteArray(temp);
+ }
+
+ if (arr && !repost) {
+ n = arr->size();
+ if (toDevice) {
+ qint64 bytesWritten;
+ bytesWritten = toDevice->write(*arr, n);
+ delete arr;
+ arr = 0;
+ // if writing to the device does not succeed, quit with error
+ if (bytesWritten == -1 || bytesWritten < n) {
+ finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Error writing response to device")), QHttp::UnknownError);
+ } else {
+ bytesDone += bytesWritten;
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::_q_slotReadyRead(): read %lld bytes (%lld bytes done)", n, bytesDone);
+#endif
+ }
+ if (response.hasContentLength())
+ emit q->dataReadProgress(bytesDone, response.contentLength());
+ else
+ emit q->dataReadProgress(bytesDone, 0);
+ } else {
+ char *ptr = rba.reserve(arr->size());
+ memcpy(ptr, arr->data(), arr->size());
+ delete arr;
+ arr = 0;
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp::_q_slotReadyRead(): read %lld bytes (%lld bytes done)", n, bytesDone + q->bytesAvailable());
+#endif
+ if (response.hasContentLength())
+ emit q->dataReadProgress(bytesDone + q->bytesAvailable(), response.contentLength());
+ else
+ emit q->dataReadProgress(bytesDone + q->bytesAvailable(), 0);
+ emit q->readyRead(response);
+ }
+ }
+
+ delete arr;
+ }
+
+ if (everythingRead) {
+ if (repost) {
+ _q_slotSendRequest();
+ return;
+ }
+ // Handle "Connection: close"
+ if (response.value(QLatin1String("connection")).toLower() == QLatin1String("close")) {
+ closeConn();
+ } else {
+ setState(QHttp::Connected);
+ // Start a timer, so that we emit the keep alive signal
+ // "after" this method returned.
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+ }
+ }
+}
+
+void QHttpPrivate::_q_slotDoFinished()
+{
+ if (state == QHttp::Connected) {
+ finishedWithSuccess();
+ } else if (state != QHttp::Unconnected) {
+ setState(QHttp::Unconnected);
+ finishedWithSuccess();
+ }
+}
+
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+QHttp::State QHttp::state() const
+{
+ Q_D(const QHttp);
+ return d->state;
+}
+
+/*!
+ Returns the last error that occurred. This is useful to find out
+ what happened when receiving a requestFinished() or a done()
+ signal with the \c error argument \c true.
+
+ If you start a new request, the error status is reset to \c NoError.
+*/
+QHttp::Error QHttp::error() const
+{
+ Q_D(const QHttp);
+ return d->error;
+}
+
+/*!
+ Returns a human-readable description of the last error that
+ occurred. This is useful to present a error message to the user
+ when receiving a requestFinished() or a done() signal with the \c
+ error argument \c true.
+*/
+QString QHttp::errorString() const
+{
+ Q_D(const QHttp);
+ return d->errorString;
+}
+
+void QHttpPrivate::setState(int s)
+{
+ Q_Q(QHttp);
+#if defined(QHTTP_DEBUG)
+ qDebug("QHttp state changed %d -> %d", state, s);
+#endif
+ state = QHttp::State(s);
+ emit q->stateChanged(s);
+}
+
+void QHttpPrivate::closeConn()
+{
+ Q_Q(QHttp);
+ // If no connection is open -> ignore
+ if (state == QHttp::Closing || state == QHttp::Unconnected)
+ return;
+
+ postDevice = 0;
+ setState(QHttp::Closing);
+
+ // Already closed ?
+ if (!socket || !socket->isOpen()) {
+ QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+ } else {
+ // Close now.
+ socket->close();
+ }
+}
+
+void QHttpPrivate::setSock(QTcpSocket *sock)
+{
+ Q_Q(const QHttp);
+
+ // disconnect all existing signals
+ if (socket)
+ socket->disconnect();
+ if (deleteSocket)
+ delete socket;
+
+ // use the new QTcpSocket socket, or create one if socket is 0.
+ deleteSocket = (sock == 0);
+ socket = sock;
+ if (!socket) {
+#ifndef QT_NO_OPENSSL
+ if (QSslSocket::supportsSsl())
+ socket = new QSslSocket();
+ else
+#endif
+ socket = new QTcpSocket();
+ }
+
+ // connect all signals
+ QObject::connect(socket, SIGNAL(connected()), q, SLOT(_q_slotConnected()));
+ QObject::connect(socket, SIGNAL(disconnected()), q, SLOT(_q_slotClosed()));
+ QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(_q_slotReadyRead()));
+ QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), q, SLOT(_q_slotError(QAbstractSocket::SocketError)));
+ QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
+ q, SLOT(_q_slotBytesWritten(qint64)));
+#ifndef QT_NO_NETWORKPROXY
+ QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+#endif
+
+#ifndef QT_NO_OPENSSL
+ if (qobject_cast<QSslSocket *>(socket)) {
+ QObject::connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ q, SIGNAL(sslErrors(QList<QSslError>)));
+ QObject::connect(socket, SIGNAL(encryptedBytesWritten(qint64)),
+ q, SLOT(_q_slotEncryptedBytesWritten(qint64)));
+ }
+#endif
+}
+
+/*!
+ Tells the QSslSocket used for the Http connection to ignore the errors
+ reported in the sslErrors() signal.
+
+ Note that this function must be called from within a slot connected to the
+ sslErrors() signal to have any effect.
+
+ \sa QSslSocket QSslSocket::sslErrors()
+*/
+#ifndef QT_NO_OPENSSL
+void QHttp::ignoreSslErrors()
+{
+ Q_D(QHttp);
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(d->socket);
+ if (sslSocket)
+ sslSocket->ignoreSslErrors();
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qhttp.cpp"
+
+#endif
diff --git a/src/qhttp/qhttp.h b/src/qhttp/qhttp.h
new file mode 100644
index 0000000..9018b76
--- /dev/null
+++ b/src/qhttp/qhttp.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTP_H
+#define QHTTP_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Network)
+
+#ifndef QT_NO_HTTP
+
+class QTcpSocket;
+class QTimerEvent;
+class QIODevice;
+class QAuthenticator;
+class QNetworkProxy;
+class QSslError;
+
+class QHttpPrivate;
+
+class QHttpHeaderPrivate;
+class Q_NETWORK_EXPORT QHttpHeader
+{
+public:
+ QHttpHeader();
+ QHttpHeader(const QHttpHeader &header);
+ QHttpHeader(const QString &str);
+ virtual ~QHttpHeader();
+
+ QHttpHeader &operator=(const QHttpHeader &h);
+
+ void setValue(const QString &key, const QString &value);
+ void setValues(const QList<QPair<QString, QString> > &values);
+ void addValue(const QString &key, const QString &value);
+ QList<QPair<QString, QString> > values() const;
+ bool hasKey(const QString &key) const;
+ QStringList keys() const;
+ QString value(const QString &key) const;
+ QStringList allValues(const QString &key) const;
+ void removeValue(const QString &key);
+ void removeAllValues(const QString &key);
+
+ // ### Qt 5: change to qint64
+ bool hasContentLength() const;
+ uint contentLength() const;
+ void setContentLength(int len);
+
+ bool hasContentType() const;
+ QString contentType() const;
+ void setContentType(const QString &type);
+
+ virtual QString toString() const;
+ bool isValid() const;
+
+ virtual int majorVersion() const = 0;
+ virtual int minorVersion() const = 0;
+
+protected:
+ virtual bool parseLine(const QString &line, int number);
+ bool parse(const QString &str);
+ void setValid(bool);
+
+ QHttpHeader(QHttpHeaderPrivate &dd, const QString &str = QString());
+ QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header);
+ QScopedPointer<QHttpHeaderPrivate> d_ptr;
+
+private:
+ Q_DECLARE_PRIVATE(QHttpHeader)
+};
+
+class QHttpResponseHeaderPrivate;
+class Q_NETWORK_EXPORT QHttpResponseHeader : public QHttpHeader
+{
+public:
+ QHttpResponseHeader();
+ QHttpResponseHeader(const QHttpResponseHeader &header);
+ QHttpResponseHeader(const QString &str);
+ QHttpResponseHeader(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
+ QHttpResponseHeader &operator=(const QHttpResponseHeader &header);
+
+ void setStatusLine(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
+
+ int statusCode() const;
+ QString reasonPhrase() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine(const QString &line, int number);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpResponseHeader)
+ friend class QHttpPrivate;
+};
+
+class QHttpRequestHeaderPrivate;
+class Q_NETWORK_EXPORT QHttpRequestHeader : public QHttpHeader
+{
+public:
+ QHttpRequestHeader();
+ QHttpRequestHeader(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
+ QHttpRequestHeader(const QHttpRequestHeader &header);
+ QHttpRequestHeader(const QString &str);
+ QHttpRequestHeader &operator=(const QHttpRequestHeader &header);
+
+ void setRequest(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
+
+ QString method() const;
+ QString path() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine(const QString &line, int number);
+
+private:
+ Q_DECLARE_PRIVATE(QHttpRequestHeader)
+};
+
+class Q_NETWORK_EXPORT QHttp : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ConnectionMode {
+ ConnectionModeHttp,
+ ConnectionModeHttps
+ };
+
+ explicit QHttp(QObject *parent = 0);
+ QHttp(const QString &hostname, quint16 port = 80, QObject *parent = 0);
+ QHttp(const QString &hostname, ConnectionMode mode, quint16 port = 0, QObject *parent = 0);
+ virtual ~QHttp();
+
+ enum State {
+ Unconnected,
+ HostLookup,
+ Connecting,
+ Sending,
+ Reading,
+ Connected,
+ Closing
+ };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ UnexpectedClose,
+ InvalidResponseHeader,
+ WrongContentLength,
+ Aborted,
+ AuthenticationRequiredError,
+ ProxyAuthenticationRequiredError
+ };
+
+ int setHost(const QString &hostname, quint16 port = 80);
+ int setHost(const QString &hostname, ConnectionMode mode, quint16 port = 0);
+
+ int setSocket(QTcpSocket *socket);
+ int setUser(const QString &username, const QString &password = QString());
+
+#ifndef QT_NO_NETWORKPROXY
+ int setProxy(const QString &host, int port,
+ const QString &username = QString(),
+ const QString &password = QString());
+ int setProxy(const QNetworkProxy &proxy);
+#endif
+
+ int get(const QString &path, QIODevice *to=0);
+ int post(const QString &path, QIODevice *data, QIODevice *to=0 );
+ int post(const QString &path, const QByteArray &data, QIODevice *to=0);
+ int head(const QString &path);
+ int request(const QHttpRequestHeader &header, QIODevice *device=0, QIODevice *to=0);
+ int request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to=0);
+
+ int closeConnection();
+ int close();
+
+ qint64 bytesAvailable() const;
+ qint64 read(char *data, qint64 maxlen);
+#ifdef QT3_SUPPORT
+ inline QT3_SUPPORT qint64 readBlock(char *data, quint64 maxlen)
+ { return read(data, qint64(maxlen)); }
+#endif
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice *currentSourceDevice() const;
+ QIODevice *currentDestinationDevice() const;
+ QHttpRequestHeader currentRequest() const;
+ QHttpResponseHeader lastResponse() const;
+ bool hasPendingRequests() const;
+ void clearPendingRequests();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+#ifndef QT_NO_OPENSSL
+ void ignoreSslErrors();
+#endif
+
+Q_SIGNALS:
+ void stateChanged(int);
+ void responseHeaderReceived(const QHttpResponseHeader &resp);
+ void readyRead(const QHttpResponseHeader &resp);
+
+ // ### Qt 5: change to qint64
+ void dataSendProgress(int, int);
+ void dataReadProgress(int, int);
+
+ void requestStarted(int);
+ void requestFinished(int, bool);
+ void done(bool);
+
+#ifndef QT_NO_NETWORKPROXY
+ void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *);
+#endif
+ void authenticationRequired(const QString &hostname, quint16 port, QAuthenticator *);
+
+#ifndef QT_NO_OPENSSL
+ void sslErrors(const QList<QSslError> &errors);
+#endif
+
+private:
+ Q_DISABLE_COPY(QHttp)
+ Q_DECLARE_PRIVATE(QHttp)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotReadyRead())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotConnected())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotError(QAbstractSocket::SocketError))
+ Q_PRIVATE_SLOT(d_func(), void _q_slotClosed())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotBytesWritten(qint64 numBytes))
+#ifndef QT_NO_OPENSSL
+ Q_PRIVATE_SLOT(d_func(), void _q_slotEncryptedBytesWritten(qint64 numBytes))
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_slotDoFinished())
+ Q_PRIVATE_SLOT(d_func(), void _q_slotSendRequest())
+ Q_PRIVATE_SLOT(d_func(), void _q_continuePost())
+
+ friend class QHttpNormalRequest;
+ friend class QHttpSetHostRequest;
+ friend class QHttpSetSocketRequest;
+ friend class QHttpSetUserRequest;
+ friend class QHttpSetProxyRequest;
+ friend class QHttpCloseRequest;
+ friend class QHttpPGHRequest;
+};
+
+#endif // QT_NO_HTTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QHTTP_H
diff --git a/src/qhttp/qhttpauthenticator.cpp b/src/qhttp/qhttpauthenticator.cpp
new file mode 100644
index 0000000..0a3dddb
--- /dev/null
+++ b/src/qhttp/qhttpauthenticator.cpp
@@ -0,0 +1,1426 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qauthenticator.h>
+#include <qauthenticator_p.h>
+#include <qdebug.h>
+#include <qhash.h>
+#include <qbytearray.h>
+#include <qcryptographichash.h>
+#include <private/qhttpheader_p.h>
+#include <qiodevice.h>
+#include <qdatastream.h>
+#include <qendian.h>
+#include <qstring.h>
+#include <qdatetime.h>
+
+//#define NTLMV1_CLIENT
+
+QT_BEGIN_NAMESPACE
+
+#ifdef NTLMV1_CLIENT
+#include "../../3rdparty/des/des.cpp"
+#endif
+
+static QByteArray qNtlmPhase1();
+static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data);
+
+/*!
+ \class QAuthenticator
+ \brief The QAuthenticator class provides an authentication object.
+ \since 4.3
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ The QAuthenticator class is usually used in the
+ \l{QNetworkAccessManager::}{authenticationRequired()} and
+ \l{QNetworkAccessManager::}{proxyAuthenticationRequired()} signals of QNetworkAccessManager and
+ QAbstractSocket. The class provides a way to pass back the required
+ authentication information to the socket when accessing services that
+ require authentication.
+
+ QAuthenticator supports the following authentication methods:
+ \list
+ \o Basic
+ \o NTLM version 1
+ \o Digest-MD5
+ \endlist
+
+ Note that, in particular, NTLM version 2 is not supported.
+
+ \section1 Options
+
+ In addition to the username and password required for authentication, a
+ QAuthenticator object can also contain additional options. The
+ options() function can be used to query incoming options sent by
+ the server; the setOption() function can
+ be used to set outgoing options, to be processed by the authenticator
+ calculation. The options accepted and provided depend on the authentication
+ type (see method()).
+
+ The following tables list known incoming options as well as accepted
+ outgoing options. The list of incoming options is not exhaustive, since
+ servers may include additional information at any time. The list of
+ outgoing options is exhaustive, however, and no unknown options will be
+ treated or sent back to the server.
+
+ \section2 Basic
+
+ \table
+ \header \o Option \o Direction \o Description
+ \row \o \tt{realm} \o Incoming \o Contains the realm of the authentication, the same as realm()
+ \endtable
+
+ The Basic authentication mechanism supports no outgoing options.
+
+ \section2 NTLM version 1
+
+ The NTLM authentication mechanism currently supports no incoming or outgoing options.
+
+ \section2 Digest-MD5
+
+ \table
+ \header \o Option \o Direction \o Description
+ \row \o \tt{realm} \o Incoming \o Contains the realm of the authentication, the same as realm()
+ \endtable
+
+ The Digest-MD5 authentication mechanism supports no outgoing options.
+
+ \sa QSslSocket
+*/
+
+
+/*!
+ Constructs an empty authentication object
+*/
+QAuthenticator::QAuthenticator()
+ : d(0)
+{
+}
+
+/*!
+ Destructs the object
+*/
+QAuthenticator::~QAuthenticator()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QAuthenticator::QAuthenticator(const QAuthenticator &other)
+ : d(other.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ Assigns the contents of \a other to this authenticator.
+*/
+QAuthenticator &QAuthenticator::operator=(const QAuthenticator &other)
+{
+ if (d == other.d)
+ return *this;
+
+ if (d && !d->ref.deref())
+ delete d;
+
+ d = other.d;
+ if (d)
+ d->ref.ref();
+ return *this;
+}
+
+/*!
+ Returns true if this authenticator is identical to \a other; otherwise
+ returns false.
+*/
+bool QAuthenticator::operator==(const QAuthenticator &other) const
+{
+ if (d == other.d)
+ return true;
+ return d->user == other.d->user
+ && d->password == other.d->password
+ && d->realm == other.d->realm
+ && d->method == other.d->method
+ && d->options == other.d->options;
+}
+
+/*!
+ \fn bool QAuthenticator::operator!=(const QAuthenticator &other) const
+
+ Returns true if this authenticator is different from \a other; otherwise
+ returns false.
+*/
+
+/*!
+ returns the user used for authentication.
+*/
+QString QAuthenticator::user() const
+{
+ return d ? d->user : QString();
+}
+
+/*!
+ Sets the \a user used for authentication.
+
+ \sa QNetworkAccessManager::authenticationRequired()
+*/
+void QAuthenticator::setUser(const QString &user)
+{
+ detach();
+ int separatorPosn = 0;
+
+ switch(d->method) {
+ case QAuthenticatorPrivate::Ntlm:
+ if((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) {
+ //domain name is present
+ d->realm.clear();
+ d->userDomain = user.left(separatorPosn);
+ d->extractedUser = user.mid(separatorPosn + 1);
+ d->user = user;
+ } else {
+ d->extractedUser = user;
+ d->user = user;
+ d->realm.clear();
+ d->userDomain.clear();
+ }
+ break;
+ default:
+ d->user = user;
+ d->userDomain.clear();
+ break;
+ }
+}
+
+/*!
+ returns the password used for authentication.
+*/
+QString QAuthenticator::password() const
+{
+ return d ? d->password : QString();
+}
+
+/*!
+ Sets the \a password used for authentication.
+
+ \sa QNetworkAccessManager::authenticationRequired()
+*/
+void QAuthenticator::setPassword(const QString &password)
+{
+ detach();
+ d->password = password;
+}
+
+/*!
+ \internal
+*/
+void QAuthenticator::detach()
+{
+ if (!d) {
+ d = new QAuthenticatorPrivate;
+ d->ref.store(1);
+ return;
+ }
+
+ qAtomicDetach(d);
+ d->phase = QAuthenticatorPrivate::Start;
+}
+
+/*!
+ returns the realm requiring authentication.
+*/
+QString QAuthenticator::realm() const
+{
+ return d ? d->realm : QString();
+}
+
+/*!
+ \since 4.7
+ Returns the value related to option \a opt if it was set by the server.
+ See \l{QAuthenticator#Options} for more information on incoming options.
+ If option \a opt isn't found, an invalid QVariant will be returned.
+
+ \sa options(), QAuthenticator#Options
+*/
+QVariant QAuthenticator::option(const QString &opt) const
+{
+ return d ? d->options.value(opt) : QVariant();
+}
+
+/*!
+ \since 4.7
+ Returns all incoming options set in this QAuthenticator object by parsing
+ the server reply. See \l{QAuthenticator#Options} for more information
+ on incoming options.
+
+ \sa option(), QAuthenticator#Options
+*/
+QVariantHash QAuthenticator::options() const
+{
+ return d ? d->options : QVariantHash();
+}
+
+/*!
+ \since 4.7
+
+ Sets the outgoing option \a opt to value \a value.
+ See \l{QAuthenticator#Options} for more information on outgoing options.
+
+ \sa options(), option(), QAuthenticator#Options
+*/
+void QAuthenticator::setOption(const QString &opt, const QVariant &value)
+{
+ detach();
+ d->options.insert(opt, value);
+}
+
+
+/*!
+ Returns true if the authenticator is null.
+*/
+bool QAuthenticator::isNull() const
+{
+ return !d;
+}
+
+QAuthenticatorPrivate::QAuthenticatorPrivate()
+ : ref(0)
+ , method(None)
+ , hasFailed(false)
+ , phase(Start)
+ , nonceCount(0)
+{
+ cnonce = QCryptographicHash::hash(QByteArray::number(qrand(), 16) + QByteArray::number(qrand(), 16),
+ QCryptographicHash::Md5).toHex();
+ nonceCount = 0;
+}
+
+#ifndef QT_NO_HTTP
+void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy)
+{
+ const QList<QPair<QString, QString> > values = header.values();
+ QList<QPair<QByteArray, QByteArray> > rawValues;
+
+ QList<QPair<QString, QString> >::const_iterator it, end;
+ for (it = values.constBegin(), end = values.constEnd(); it != end; ++it)
+ rawValues.append(qMakePair(it->first.toLatin1(), it->second.toUtf8()));
+
+ // continue in byte array form
+ parseHttpResponse(rawValues, isProxy);
+}
+#endif
+
+void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy)
+{
+ const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
+
+ method = None;
+ /*
+ Fun from the HTTP 1.1 specs, that we currently ignore:
+
+ User agents are advised to take special care in parsing the WWW-
+ Authenticate field value as it might contain more than one challenge,
+ or if more than one WWW-Authenticate header field is provided, the
+ contents of a challenge itself can contain a comma-separated list of
+ authentication parameters.
+ */
+
+ QByteArray headerVal;
+ for (int i = 0; i < values.size(); ++i) {
+ const QPair<QByteArray, QByteArray> &current = values.at(i);
+ if (current.first.toLower() != search)
+ continue;
+ QByteArray str = current.second.toLower();
+ if (method < Basic && str.startsWith("basic")) {
+ method = Basic;
+ headerVal = current.second.mid(6);
+ } else if (method < Ntlm && str.startsWith("ntlm")) {
+ method = Ntlm;
+ headerVal = current.second.mid(5);
+ } else if (method < DigestMd5 && str.startsWith("digest")) {
+ method = DigestMd5;
+ headerVal = current.second.mid(7);
+ }
+ }
+
+ challenge = headerVal.trimmed();
+ QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
+
+ switch(method) {
+ case Basic:
+ this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ if (user.isEmpty() && password.isEmpty())
+ phase = Done;
+ break;
+ case Ntlm:
+ // #### extract from header
+ break;
+ case DigestMd5: {
+ this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm"));
+ if (options.value("stale").toLower() == "true")
+ phase = Start;
+ if (user.isEmpty() && password.isEmpty())
+ phase = Done;
+ break;
+ }
+ default:
+ realm.clear();
+ challenge = QByteArray();
+ phase = Invalid;
+ }
+}
+
+QByteArray QAuthenticatorPrivate::calculateResponse(const QByteArray &requestMethod, const QByteArray &path)
+{
+ QByteArray response;
+ const char *methodString = 0;
+ switch(method) {
+ case QAuthenticatorPrivate::None:
+ methodString = "";
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Plain:
+ response = '\0' + user.toUtf8() + '\0' + password.toUtf8();
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Basic:
+ methodString = "Basic ";
+ response = user.toLatin1() + ':' + password.toLatin1();
+ response = response.toBase64();
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Login:
+ if (challenge.contains("VXNlciBOYW1lAA==")) {
+ response = user.toUtf8().toBase64();
+ phase = Phase2;
+ } else if (challenge.contains("UGFzc3dvcmQA")) {
+ response = password.toUtf8().toBase64();
+ phase = Done;
+ }
+ break;
+ case QAuthenticatorPrivate::CramMd5:
+ break;
+ case QAuthenticatorPrivate::DigestMd5:
+ methodString = "Digest ";
+ response = digestMd5Response(challenge, requestMethod, path);
+ phase = Done;
+ break;
+ case QAuthenticatorPrivate::Ntlm:
+ methodString = "NTLM ";
+ if (challenge.isEmpty()) {
+ response = qNtlmPhase1().toBase64();
+ if (user.isEmpty())
+ phase = Done;
+ else
+ phase = Phase2;
+ } else {
+ response = qNtlmPhase3(this, QByteArray::fromBase64(challenge)).toBase64();
+ phase = Done;
+ }
+
+ break;
+ }
+ return QByteArray(methodString) + response;
+}
+
+
+// ---------------------------- Digest Md5 code ----------------------------------------
+
+QHash<QByteArray, QByteArray> QAuthenticatorPrivate::parseDigestAuthenticationChallenge(const QByteArray &challenge)
+{
+ QHash<QByteArray, QByteArray> options;
+ // parse the challenge
+ const char *d = challenge.constData();
+ const char *end = d + challenge.length();
+ while (d < end) {
+ while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
+ ++d;
+ const char *start = d;
+ while (d < end && *d != '=')
+ ++d;
+ QByteArray key = QByteArray(start, d - start);
+ ++d;
+ if (d >= end)
+ break;
+ bool quote = (*d == '"');
+ if (quote)
+ ++d;
+ if (d >= end)
+ break;
+ start = d;
+ QByteArray value;
+ while (d < end) {
+ bool backslash = false;
+ if (*d == '\\' && d < end - 1) {
+ ++d;
+ backslash = true;
+ }
+ if (!backslash) {
+ if (quote) {
+ if (*d == '"')
+ break;
+ } else {
+ if (*d == ',')
+ break;
+ }
+ }
+ value += *d;
+ ++d;
+ }
+ while (d < end && *d != ',')
+ ++d;
+ ++d;
+ options[key] = value;
+ }
+
+ QByteArray qop = options.value("qop");
+ if (!qop.isEmpty()) {
+ QList<QByteArray> qopoptions = qop.split(',');
+ if (!qopoptions.contains("auth"))
+ return QHash<QByteArray, QByteArray>();
+ // #### can't do auth-int currently
+// if (qop.contains("auth-int"))
+// qop = "auth-int";
+// else if (qop.contains("auth"))
+// qop = "auth";
+// else
+// qop = QByteArray();
+ options["qop"] = "auth";
+ }
+
+ return options;
+}
+
+/*
+ Digest MD5 implementation
+
+ Code taken from RFC 2617
+
+ Currently we don't support the full SASL authentication mechanism (which includes cyphers)
+*/
+
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+static QByteArray digestMd5ResponseHelper(
+ const QByteArray &alg,
+ const QByteArray &userName,
+ const QByteArray &realm,
+ const QByteArray &password,
+ const QByteArray &nonce, /* nonce from server */
+ const QByteArray &nonceCount, /* 8 hex digits */
+ const QByteArray &cNonce, /* client nonce */
+ const QByteArray &qop, /* qop-value: "", "auth", "auth-int" */
+ const QByteArray &method, /* method from the request */
+ const QByteArray &digestUri, /* requested URL */
+ const QByteArray &hEntity /* H(entity body) if qop="auth-int" */
+ )
+{
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ hash.addData(userName);
+ hash.addData(":", 1);
+ hash.addData(realm);
+ hash.addData(":", 1);
+ hash.addData(password);
+ QByteArray ha1 = hash.result();
+ if (alg.toLower() == "md5-sess") {
+ hash.reset();
+ // RFC 2617 contains an error, it was:
+ // hash.addData(ha1);
+ // but according to the errata page at http://www.rfc-editor.org/errata_list.php, ID 1649, it
+ // must be the following line:
+ hash.addData(ha1.toHex());
+ hash.addData(":", 1);
+ hash.addData(nonce);
+ hash.addData(":", 1);
+ hash.addData(cNonce);
+ ha1 = hash.result();
+ };
+ ha1 = ha1.toHex();
+
+ // calculate H(A2)
+ hash.reset();
+ hash.addData(method);
+ hash.addData(":", 1);
+ hash.addData(digestUri);
+ if (qop.toLower() == "auth-int") {
+ hash.addData(":", 1);
+ hash.addData(hEntity);
+ }
+ QByteArray ha2hex = hash.result().toHex();
+
+ // calculate response
+ hash.reset();
+ hash.addData(ha1);
+ hash.addData(":", 1);
+ hash.addData(nonce);
+ hash.addData(":", 1);
+ if (!qop.isNull()) {
+ hash.addData(nonceCount);
+ hash.addData(":", 1);
+ hash.addData(cNonce);
+ hash.addData(":", 1);
+ hash.addData(qop);
+ hash.addData(":", 1);
+ }
+ hash.addData(ha2hex);
+ return hash.result().toHex();
+}
+
+QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path)
+{
+ QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge);
+
+ ++nonceCount;
+ QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
+ while (nonceCountString.length() < 8)
+ nonceCountString.prepend('0');
+
+ QByteArray nonce = options.value("nonce");
+ QByteArray opaque = options.value("opaque");
+ QByteArray qop = options.value("qop");
+
+// qDebug() << "calculating digest: method=" << method << "path=" << path;
+ QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(),
+ realm.toLatin1(), password.toLatin1(),
+ nonce, nonceCountString,
+ cnonce, qop, method,
+ path, QByteArray());
+
+
+ QByteArray credentials;
+ credentials += "username=\"" + user.toLatin1() + "\", ";
+ credentials += "realm=\"" + realm.toLatin1() + "\", ";
+ credentials += "nonce=\"" + nonce + "\", ";
+ credentials += "uri=\"" + path + "\", ";
+ if (!opaque.isEmpty())
+ credentials += "opaque=\"" + opaque + "\", ";
+ credentials += "response=\"" + response + '\"';
+ if (!options.value("algorithm").isEmpty())
+ credentials += ", algorithm=" + options.value("algorithm");
+ if (!options.value("qop").isEmpty()) {
+ credentials += ", qop=" + qop + ", ";
+ credentials += "nc=" + nonceCountString + ", ";
+ credentials += "cnonce=\"" + cnonce + '\"';
+ }
+
+ return credentials;
+}
+
+// ---------------------------- Digest Md5 code ----------------------------------------
+
+
+
+/*
+ * NTLM message flags.
+ *
+ * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
+ *
+ * This software is released under the MIT license.
+ */
+
+/*
+ * Indicates that Unicode strings are supported for use in security
+ * buffer data.
+ */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
+
+/*
+ * Indicates that OEM strings are supported for use in security buffer data.
+ */
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002
+
+/*
+ * Requests that the server's authentication realm be included in the
+ * Type 2 message.
+ */
+#define NTLMSSP_REQUEST_TARGET 0x00000004
+
+/*
+ * Specifies that authenticated communication between the client and server
+ * should carry a digital signature (message integrity).
+ */
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
+
+/*
+ * Specifies that authenticated communication between the client and server
+ * should be encrypted (message confidentiality).
+ */
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
+
+/*
+ * Indicates that datagram authentication is being used.
+ */
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
+
+/*
+ * Indicates that the LAN Manager session key should be
+ * used for signing and sealing authenticated communications.
+ */
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
+
+/*
+ * Indicates that NTLM authentication is being used.
+ */
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
+
+/*
+ * Sent by the client in the Type 1 message to indicate that the name of the
+ * domain in which the client workstation has membership is included in the
+ * message. This is used by the server to determine whether the client is
+ * eligible for local authentication.
+ */
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
+
+/*
+ * Sent by the client in the Type 1 message to indicate that the client
+ * workstation's name is included in the message. This is used by the server
+ * to determine whether the client is eligible for local authentication.
+ */
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
+
+/*
+ * Sent by the server to indicate that the server and client are on the same
+ * machine. Implies that the client may use the established local credentials
+ * for authentication instead of calculating a response to the challenge.
+ */
+#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x00004000
+
+/*
+ * Indicates that authenticated communication between the client and server
+ * should be signed with a "dummy" signature.
+ */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a domain.
+ */
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a server.
+ */
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that the target
+ * authentication realm is a share. Presumably, this is for share-level
+ * authentication. Usage is unclear.
+ */
+#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
+
+/*
+ * Indicates that the NTLM2 signing and sealing scheme should be used for
+ * protecting authenticated communications. Note that this refers to a
+ * particular session security scheme, and is not related to the use of
+ * NTLMv2 authentication.
+ */
+#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
+
+/*
+ * Sent by the server in the Type 2 message to indicate that it is including
+ * a Target Information block in the message. The Target Information block
+ * is used in the calculation of the NTLMv2 response.
+ */
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
+
+/*
+ * Indicates that 128-bit encryption is supported.
+ */
+#define NTLMSSP_NEGOTIATE_128 0x20000000
+
+/*
+ * Indicates that the client will provide an encrypted master session key in
+ * the "Session Key" field of the Type 3 message. This is used in signing and
+ * sealing, and is RC4-encrypted using the previous session key as the
+ * encryption key.
+ */
+#define NTLMSSP_NEGOTIATE_KEY_EXCHANGE 0x40000000
+
+/*
+ * Indicates that 56-bit encryption is supported.
+ */
+#define NTLMSSP_NEGOTIATE_56 0x80000000
+
+/*
+ * AvId values
+ */
+#define AVTIMESTAMP 7
+
+//#define NTLMV1_CLIENT
+
+
+//************************Global variables***************************
+
+const int blockSize = 64; //As per RFC2104 Block-size is 512 bits
+const int nDigestLen = 16; //Trunctaion Length of the Hmac-Md5 digest
+const quint8 respversion = 1;
+const quint8 hirespversion = 1;
+
+/* usage:
+ // fill up ctx with what we know.
+ QByteArray response = qNtlmPhase1(ctx);
+ // send response (b64 encoded??)
+ // get response from server (b64 decode?)
+ Phase2Block pb;
+ qNtlmDecodePhase2(response, pb);
+ response = qNtlmPhase3(ctx, pb);
+ // send response (b64 encoded??)
+*/
+
+/*
+ TODO:
+ - Fix unicode handling
+ - add v2 handling
+*/
+
+class QNtlmBuffer {
+public:
+ QNtlmBuffer() : len(0), maxLen(0), offset(0) {}
+ quint16 len;
+ quint16 maxLen;
+ quint32 offset;
+ enum { Size = 8 };
+};
+
+class QNtlmPhase1BlockBase
+{
+public:
+ char magic[8];
+ quint32 type;
+ quint32 flags;
+ QNtlmBuffer domain;
+ QNtlmBuffer workstation;
+ enum { Size = 32 };
+};
+
+// ################# check paddings
+class QNtlmPhase2BlockBase
+{
+public:
+ char magic[8];
+ quint32 type;
+ QNtlmBuffer targetName;
+ quint32 flags;
+ unsigned char challenge[8];
+ quint32 context[2];
+ QNtlmBuffer targetInfo;
+ enum { Size = 48 };
+};
+
+class QNtlmPhase3BlockBase {
+public:
+ char magic[8];
+ quint32 type;
+ QNtlmBuffer lmResponse;
+ QNtlmBuffer ntlmResponse;
+ QNtlmBuffer domain;
+ QNtlmBuffer user;
+ QNtlmBuffer workstation;
+ QNtlmBuffer sessionKey;
+ quint32 flags;
+ enum { Size = 64 };
+};
+
+static void qStreamNtlmBuffer(QDataStream& ds, const QByteArray& s)
+{
+ ds.writeRawData(s.constData(), s.size());
+}
+
+
+static void qStreamNtlmString(QDataStream& ds, const QString& s, bool unicode)
+{
+ if (!unicode) {
+ qStreamNtlmBuffer(ds, s.toLatin1());
+ return;
+ }
+ const ushort *d = s.utf16();
+ for (int i = 0; i < s.length(); ++i)
+ ds << d[i];
+}
+
+
+
+static int qEncodeNtlmBuffer(QNtlmBuffer& buf, int offset, const QByteArray& s)
+{
+ buf.len = s.size();
+ buf.maxLen = buf.len;
+ buf.offset = (offset + 1) & ~1;
+ return buf.offset + buf.len;
+}
+
+
+static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, bool unicode)
+{
+ if (!unicode)
+ return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
+ buf.len = 2 * s.length();
+ buf.maxLen = buf.len;
+ buf.offset = (offset + 1) & ~1;
+ return buf.offset + buf.len;
+}
+
+
+static QDataStream& operator<<(QDataStream& s, const QNtlmBuffer& b)
+{
+ s << b.len << b.maxLen << b.offset;
+ return s;
+}
+
+static QDataStream& operator>>(QDataStream& s, QNtlmBuffer& b)
+{
+ s >> b.len >> b.maxLen >> b.offset;
+ return s;
+}
+
+
+class QNtlmPhase1Block : public QNtlmPhase1BlockBase
+{ // request
+public:
+ QNtlmPhase1Block() {
+ qstrncpy(magic, "NTLMSSP", 8);
+ type = 1;
+ flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET;
+ }
+
+ // extracted
+ QString domainStr, workstationStr;
+};
+
+
+class QNtlmPhase2Block : public QNtlmPhase2BlockBase
+{ // challenge
+public:
+ QNtlmPhase2Block() {
+ magic[0] = 0;
+ type = 0xffffffff;
+ }
+
+ // extracted
+ QString targetNameStr, targetInfoStr;
+ QByteArray targetInfoBuff;
+};
+
+
+
+class QNtlmPhase3Block : public QNtlmPhase3BlockBase { // response
+public:
+ QNtlmPhase3Block() {
+ qstrncpy(magic, "NTLMSSP", 8);
+ type = 3;
+ flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO;
+ }
+
+ // extracted
+ QByteArray lmResponseBuf, ntlmResponseBuf;
+ QString domainStr, userStr, workstationStr, sessionKeyStr;
+ QByteArray v2Hash;
+};
+
+
+static QDataStream& operator<<(QDataStream& s, const QNtlmPhase1Block& b) {
+ bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
+
+ s.writeRawData(b.magic, sizeof(b.magic));
+ s << b.type;
+ s << b.flags;
+ s << b.domain;
+ s << b.workstation;
+ if (!b.domainStr.isEmpty())
+ qStreamNtlmString(s, b.domainStr, unicode);
+ if (!b.workstationStr.isEmpty())
+ qStreamNtlmString(s, b.workstationStr, unicode);
+ return s;
+}
+
+
+static QDataStream& operator<<(QDataStream& s, const QNtlmPhase3Block& b) {
+ bool unicode = (b.flags & NTLMSSP_NEGOTIATE_UNICODE);
+ s.writeRawData(b.magic, sizeof(b.magic));
+ s << b.type;
+ s << b.lmResponse;
+ s << b.ntlmResponse;
+ s << b.domain;
+ s << b.user;
+ s << b.workstation;
+ s << b.sessionKey;
+ s << b.flags;
+
+ if (!b.domainStr.isEmpty())
+ qStreamNtlmString(s, b.domainStr, unicode);
+
+ qStreamNtlmString(s, b.userStr, unicode);
+
+ if (!b.workstationStr.isEmpty())
+ qStreamNtlmString(s, b.workstationStr, unicode);
+
+ // Send auth info
+ qStreamNtlmBuffer(s, b.lmResponseBuf);
+ qStreamNtlmBuffer(s, b.ntlmResponseBuf);
+
+
+ return s;
+}
+
+
+static QByteArray qNtlmPhase1()
+{
+ QByteArray rc;
+ QDataStream ds(&rc, QIODevice::WriteOnly);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ QNtlmPhase1Block pb;
+ ds << pb;
+ return rc;
+}
+
+
+static QByteArray qStringAsUcs2Le(const QString& src)
+{
+ QByteArray rc(2*src.length(), 0);
+ const unsigned short *s = src.utf16();
+ unsigned short *d = (unsigned short*)rc.data();
+ for (int i = 0; i < src.length(); ++i) {
+ d[i] = qToLittleEndian(s[i]);
+ }
+ return rc;
+}
+
+
+static QString qStringFromUcs2Le(const QByteArray& src)
+{
+ Q_ASSERT(src.size() % 2 == 0);
+ unsigned short *d = (unsigned short*)src.data();
+ for (int i = 0; i < src.length() / 2; ++i) {
+ d[i] = qFromLittleEndian(d[i]);
+ }
+ return QString((const QChar *)src.data(), src.size()/2);
+}
+
+#ifdef NTLMV1_CLIENT
+static QByteArray qEncodeNtlmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch)
+{
+ QCryptographicHash md4(QCryptographicHash::Md4);
+ QByteArray asUcs2Le = qStringAsUcs2Le(ctx->password);
+ md4.addData(asUcs2Le.data(), asUcs2Le.size());
+
+ unsigned char md4hash[22];
+ memset(md4hash, 0, sizeof(md4hash));
+ QByteArray hash = md4.result();
+ Q_ASSERT(hash.size() == 16);
+ memcpy(md4hash, hash.constData(), 16);
+
+ QByteArray rc(24, 0);
+ deshash((unsigned char *)rc.data(), md4hash, (unsigned char *)ch.challenge);
+ deshash((unsigned char *)rc.data() + 8, md4hash + 7, (unsigned char *)ch.challenge);
+ deshash((unsigned char *)rc.data() + 16, md4hash + 14, (unsigned char *)ch.challenge);
+
+ hash.fill(0);
+ return rc;
+}
+
+
+static QByteArray qEncodeLmResponse(const QAuthenticatorPrivate *ctx, const QNtlmPhase2Block& ch)
+{
+ QByteArray hash(21, 0);
+ QByteArray key(14, 0);
+ qstrncpy(key.data(), ctx->password.toUpper().toLatin1(), 14);
+ const char *block = "KGS!@#$%";
+
+ deshash((unsigned char *)hash.data(), (unsigned char *)key.data(), (unsigned char *)block);
+ deshash((unsigned char *)hash.data() + 8, (unsigned char *)key.data() + 7, (unsigned char *)block);
+ key.fill(0);
+
+ QByteArray rc(24, 0);
+ deshash((unsigned char *)rc.data(), (unsigned char *)hash.data(), ch.challenge);
+ deshash((unsigned char *)rc.data() + 8, (unsigned char *)hash.data() + 7, ch.challenge);
+ deshash((unsigned char *)rc.data() + 16, (unsigned char *)hash.data() + 14, ch.challenge);
+
+ hash.fill(0);
+ return rc;
+}
+#endif
+
+/*********************************************************************
+* Function Name: qEncodeHmacMd5
+* Params:
+* key: Type - QByteArray
+* - It is the Authentication key
+* message: Type - QByteArray
+* - This is the actual message which will be encoded
+* using HMacMd5 hash algorithm
+*
+* Return Value:
+* hmacDigest: Type - QByteArray
+*
+* Description:
+* This function will be used to encode the input message using
+* HMacMd5 hash algorithm.
+*
+* As per the RFC2104 the HMacMd5 algorithm can be specified
+* ---------------------------------------
+* MD5(K XOR opad, MD5(K XOR ipad, text))
+* ---------------------------------------
+*
+*********************************************************************/
+QByteArray qEncodeHmacMd5(QByteArray &key, const QByteArray &message)
+{
+ Q_ASSERT_X(!(message.isEmpty()),"qEncodeHmacMd5", "Empty message check");
+ Q_ASSERT_X(!(key.isEmpty()),"qEncodeHmacMd5", "Empty key check");
+
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ QByteArray hMsg;
+
+ QByteArray iKeyPad(blockSize, 0x36);
+ QByteArray oKeyPad(blockSize, 0x5c);
+
+ hash.reset();
+ // Adjust the key length to blockSize
+
+ if(blockSize < key.length()) {
+ hash.addData(key);
+ key = hash.result(); //MD5 will always return 16 bytes length output
+ }
+
+ //Key will be <= 16 or 20 bytes as hash function (MD5 or SHA hash algorithms)
+ //key size can be max of Block size only
+ key = key.leftJustified(blockSize,0,true);
+
+ //iKeyPad, oKeyPad and key are all of same size "blockSize"
+
+ //xor of iKeyPad with Key and store the result into iKeyPad
+ for(int i = 0; i<key.size();i++) {
+ iKeyPad[i] = key[i]^iKeyPad[i];
+ }
+
+ //xor of oKeyPad with Key and store the result into oKeyPad
+ for(int i = 0; i<key.size();i++) {
+ oKeyPad[i] = key[i]^oKeyPad[i];
+ }
+
+ iKeyPad.append(message); // (K0 xor ipad) || text
+
+ hash.reset();
+ hash.addData(iKeyPad);
+ hMsg = hash.result();
+ //Digest gen after pass-1: H((K0 xor ipad)||text)
+
+ QByteArray hmacDigest;
+ oKeyPad.append(hMsg);
+ hash.reset();
+ hash.addData(oKeyPad);
+ hmacDigest = hash.result();
+ // H((K0 xor opad )|| H((K0 xor ipad) || text))
+
+ /*hmacDigest should not be less than half the length of the HMAC output
+ (to match the birthday attack bound) and not less than 80 bits
+ (a suitable lower bound on the number of bits that need to be
+ predicted by an attacker).
+ Refer RFC 2104 for more details on truncation part */
+
+ /*MD5 hash always returns 16 byte digest only and HMAC-MD5 spec
+ (RFC 2104) also says digest length should be 16 bytes*/
+ return hmacDigest;
+}
+
+static QByteArray qCreatev2Hash(const QAuthenticatorPrivate *ctx,
+ QNtlmPhase3Block *phase3)
+{
+ Q_ASSERT(phase3 != 0);
+ // since v2 Hash is need for both NTLMv2 and LMv2 it is calculated
+ // only once and stored and reused
+ if(phase3->v2Hash.size() == 0) {
+ QCryptographicHash md4(QCryptographicHash::Md4);
+ QByteArray passUnicode = qStringAsUcs2Le(ctx->password);
+ md4.addData(passUnicode.data(), passUnicode.size());
+
+ QByteArray hashKey = md4.result();
+ Q_ASSERT(hashKey.size() == 16);
+ // Assuming the user and domain is always unicode in challenge
+ QByteArray message =
+ qStringAsUcs2Le(ctx->extractedUser.toUpper()) +
+ qStringAsUcs2Le(phase3->domainStr);
+
+ phase3->v2Hash = qEncodeHmacMd5(hashKey, message);
+ }
+ return phase3->v2Hash;
+}
+
+static QByteArray clientChallenge(const QAuthenticatorPrivate *ctx)
+{
+ Q_ASSERT(ctx->cnonce.size() >= 8);
+ QByteArray clientCh = ctx->cnonce.right(8);
+ return clientCh;
+}
+
+// caller has to ensure a valid targetInfoBuff
+static QByteArray qExtractServerTime(const QByteArray& targetInfoBuff)
+{
+ QByteArray timeArray;
+ QDataStream ds(targetInfoBuff);
+ ds.setByteOrder(QDataStream::LittleEndian);
+
+ quint16 avId;
+ quint16 avLen;
+
+ ds >> avId;
+ ds >> avLen;
+ while(avId != 0) {
+ if(avId == AVTIMESTAMP) {
+ timeArray.resize(avLen);
+ //avLen size of QByteArray is allocated
+ ds.readRawData(timeArray.data(), avLen);
+ break;
+ }
+ ds.skipRawData(avLen);
+ ds >> avId;
+ ds >> avLen;
+ }
+ return timeArray;
+}
+
+static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx,
+ const QNtlmPhase2Block& ch,
+ QNtlmPhase3Block *phase3)
+{
+ Q_ASSERT(phase3 != 0);
+ // return value stored in phase3
+ qCreatev2Hash(ctx, phase3);
+
+ QByteArray temp;
+ QDataStream ds(&temp, QIODevice::WriteOnly);
+ ds.setByteOrder(QDataStream::LittleEndian);
+
+ ds << respversion;
+ ds << hirespversion;
+
+ //Reserved
+ QByteArray reserved1(6, 0);
+ ds.writeRawData(reserved1.constData(), reserved1.size());
+
+ quint64 time = 0;
+ QByteArray timeArray;
+
+ if(ch.targetInfo.len)
+ {
+ timeArray = qExtractServerTime(ch.targetInfoBuff);
+ }
+
+ //if server sends time, use it instead of current time
+ if(timeArray.size()) {
+ ds.writeRawData(timeArray.constData(), timeArray.size());
+ } else {
+ QDateTime currentTime(QDate::currentDate(),
+ QTime::currentTime(), Qt::UTC);
+
+ // number of seconds between 1601 and epoc(1970)
+ // 369 years, 89 leap years
+ // ((369 * 365) + 89) * 24 * 3600 = 11644473600
+
+ time = Q_UINT64_C(currentTime.toTime_t() + 11644473600);
+
+ // represented as 100 nano seconds
+ time = Q_UINT64_C(time * 10000000);
+ ds << time;
+ }
+
+ //8 byte client challenge
+ QByteArray clientCh = clientChallenge(ctx);
+ ds.writeRawData(clientCh.constData(), clientCh.size());
+
+ //Reserved
+ QByteArray reserved2(4, 0);
+ ds.writeRawData(reserved2.constData(), reserved2.size());
+
+ if (ch.targetInfo.len > 0) {
+ ds.writeRawData(ch.targetInfoBuff.constData(),
+ ch.targetInfoBuff.size());
+ }
+
+ //Reserved
+ QByteArray reserved3(4, 0);
+ ds.writeRawData(reserved3.constData(), reserved3.size());
+
+ QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
+ message.append(temp);
+
+ QByteArray ntChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
+ ntChallengeResp.append(temp);
+
+ return ntChallengeResp;
+}
+
+static QByteArray qEncodeLmv2Response(const QAuthenticatorPrivate *ctx,
+ const QNtlmPhase2Block& ch,
+ QNtlmPhase3Block *phase3)
+{
+ Q_ASSERT(phase3 != 0);
+ // return value stored in phase3
+ qCreatev2Hash(ctx, phase3);
+
+ QByteArray message((const char*)ch.challenge, sizeof(ch.challenge));
+ QByteArray clientCh = clientChallenge(ctx);
+
+ message.append(clientCh);
+
+ QByteArray lmChallengeResp = qEncodeHmacMd5(phase3->v2Hash, message);
+ lmChallengeResp.append(clientCh);
+
+ return lmChallengeResp;
+}
+
+static bool qNtlmDecodePhase2(const QByteArray& data, QNtlmPhase2Block& ch)
+{
+ Q_ASSERT(QNtlmPhase2BlockBase::Size == sizeof(QNtlmPhase2BlockBase));
+ if (data.size() < QNtlmPhase2BlockBase::Size)
+ return false;
+
+
+ QDataStream ds(data);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ if (ds.readRawData(ch.magic, 8) < 8)
+ return false;
+ if (strncmp(ch.magic, "NTLMSSP", 8) != 0)
+ return false;
+
+ ds >> ch.type;
+ if (ch.type != 2)
+ return false;
+
+ ds >> ch.targetName;
+ ds >> ch.flags;
+ if (ds.readRawData((char *)ch.challenge, 8) < 8)
+ return false;
+ ds >> ch.context[0] >> ch.context[1];
+ ds >> ch.targetInfo;
+
+ if (ch.targetName.len > 0) {
+ if (ch.targetName.len + ch.targetName.offset >= (unsigned)data.size())
+ return false;
+
+ ch.targetNameStr = qStringFromUcs2Le(data.mid(ch.targetName.offset, ch.targetName.len));
+ }
+
+ if (ch.targetInfo.len > 0) {
+ if (ch.targetInfo.len + ch.targetInfo.offset > (unsigned)data.size())
+ return false;
+
+ ch.targetInfoBuff = data.mid(ch.targetInfo.offset, ch.targetInfo.len);
+ }
+
+ return true;
+}
+
+
+static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phase2data)
+{
+ QNtlmPhase2Block ch;
+ if (!qNtlmDecodePhase2(phase2data, ch))
+ return QByteArray();
+
+ QByteArray rc;
+ QDataStream ds(&rc, QIODevice::WriteOnly);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ QNtlmPhase3Block pb;
+
+ bool unicode = ch.flags & NTLMSSP_NEGOTIATE_UNICODE;
+
+ pb.flags = NTLMSSP_NEGOTIATE_NTLM;
+ if (unicode)
+ pb.flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ else
+ pb.flags |= NTLMSSP_NEGOTIATE_OEM;
+
+
+ int offset = QNtlmPhase3BlockBase::Size;
+ Q_ASSERT(QNtlmPhase3BlockBase::Size == sizeof(QNtlmPhase3BlockBase));
+
+ // for kerberos style user@domain logins, NTLM domain string should be left empty
+ if (ctx->userDomain.isEmpty() && !ctx->extractedUser.contains(QLatin1Char('@'))) {
+ offset = qEncodeNtlmString(pb.domain, offset, ch.targetNameStr, unicode);
+ pb.domainStr = ch.targetNameStr;
+ } else {
+ offset = qEncodeNtlmString(pb.domain, offset, ctx->userDomain, unicode);
+ pb.domainStr = ctx->userDomain;
+ }
+
+ offset = qEncodeNtlmString(pb.user, offset, ctx->extractedUser, unicode);
+ pb.userStr = ctx->extractedUser;
+
+ offset = qEncodeNtlmString(pb.workstation, offset, ctx->workstation, unicode);
+ pb.workstationStr = ctx->workstation;
+
+ // Get LM response
+#ifdef NTLMV1_CLIENT
+ pb.lmResponseBuf = qEncodeLmResponse(ctx, ch);
+#else
+ if (ch.targetInfo.len > 0) {
+ pb.lmResponseBuf = QByteArray();
+ } else {
+ pb.lmResponseBuf = qEncodeLmv2Response(ctx, ch, &pb);
+ }
+#endif
+ offset = qEncodeNtlmBuffer(pb.lmResponse, offset, pb.lmResponseBuf);
+
+ // Get NTLM response
+#ifdef NTLMV1_CLIENT
+ pb.ntlmResponseBuf = qEncodeNtlmResponse(ctx, ch);
+#else
+ pb.ntlmResponseBuf = qEncodeNtlmv2Response(ctx, ch, &pb);
+#endif
+ offset = qEncodeNtlmBuffer(pb.ntlmResponse, offset, pb.ntlmResponseBuf);
+
+
+ // Encode and send
+ ds << pb;
+
+ return rc;
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/qhttp/qhttpauthenticator_p.h b/src/qhttp/qhttpauthenticator_p.h
new file mode 100644
index 0000000..4b79202
--- /dev/null
+++ b/src/qhttp/qhttpauthenticator_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QAUTHENTICATOR_P_H
+#define QAUTHENTICATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qhash.h>
+#include <qbytearray.h>
+#include <qstring.h>
+#include <qauthenticator.h>
+#include <qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpResponseHeader;
+
+class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
+{
+public:
+ enum Method { None, Basic, Plain, Login, Ntlm, CramMd5, DigestMd5 };
+ QAuthenticatorPrivate();
+
+ QAtomicInt ref;
+ QString user;
+ QString extractedUser;
+ QString password;
+ QVariantHash options;
+ Method method;
+ QString realm;
+ QByteArray challenge;
+ bool hasFailed; //credentials have been tried but rejected by server.
+
+ enum Phase {
+ Start,
+ Phase2,
+ Done,
+ Invalid
+ };
+ Phase phase;
+
+ // digest specific
+ QByteArray cnonce;
+ int nonceCount;
+
+ // ntlm specific
+ QString workstation;
+ QString userDomain;
+
+ QByteArray calculateResponse(const QByteArray &method, const QByteArray &path);
+
+ inline static QAuthenticatorPrivate *getPrivate(QAuthenticator &auth) { return auth.d; }
+ inline static const QAuthenticatorPrivate *getPrivate(const QAuthenticator &auth) { return auth.d; }
+
+ QByteArray digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path);
+ static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(const QByteArray &challenge);
+
+#ifndef QT_NO_HTTP
+ void parseHttpResponse(const QHttpResponseHeader &, bool isProxy);
+#endif
+ void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy);
+
+};
+
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qhttp/qringbuffer_p.h b/src/qhttp/qringbuffer_p.h
new file mode 100644
index 0000000..6bbce03
--- /dev/null
+++ b/src/qhttp/qringbuffer_p.h
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRINGBUFFER_P_H
+#define QRINGBUFFER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRingBuffer
+{
+public:
+ inline QRingBuffer(int growth = 4096) : basicBlockSize(growth) {
+ buffers << QByteArray();
+ clear();
+ }
+
+ inline int nextDataBlockSize() const {
+ return (tailBuffer == 0 ? tail : buffers.first().size()) - head;
+ }
+
+ inline const char *readPointer() const {
+ return buffers.isEmpty() ? 0 : (buffers.first().constData() + head);
+ }
+
+ // access the bytes at a specified position
+ // the out-variable length will contain the amount of bytes readable
+ // from there, e.g. the amount still the same QByteArray
+ inline const char *readPointerAtPosition(qint64 pos, qint64 &length) const {
+ if (buffers.isEmpty()) {
+ length = 0;
+ return 0;
+ }
+
+ if (pos >= bufferSize) {
+ length = 0;
+ return 0;
+ }
+
+ // special case: it is in the first buffer
+ int nextDataBlockSizeValue = nextDataBlockSize();
+ if (pos - head < nextDataBlockSizeValue) {
+ length = nextDataBlockSizeValue - pos;
+ return buffers.at(0).constData() + head + pos;
+ }
+
+ // special case: we only had one buffer and tried to read over it
+ if (buffers.length() == 1) {
+ length = 0;
+ return 0;
+ }
+
+ // skip the first
+ pos -= nextDataBlockSizeValue;
+
+ // normal case: it is somewhere in the second to the-one-before-the-tailBuffer
+ for (int i = 1; i < tailBuffer; i++) {
+ if (pos >= buffers[i].size()) {
+ pos -= buffers[i].size();
+ continue;
+ }
+
+ length = buffers[i].length() - pos;
+ return buffers[i].constData() + pos;
+ }
+
+ // it is in the tail buffer
+ length = tail - pos;
+ return buffers[tailBuffer].constData() + pos;
+ }
+
+ inline void free(int bytes) {
+ bufferSize -= bytes;
+ if (bufferSize < 0)
+ bufferSize = 0;
+
+ for (;;) {
+ int nextBlockSize = nextDataBlockSize();
+ if (bytes < nextBlockSize) {
+ head += bytes;
+ if (head == tail && tailBuffer == 0)
+ head = tail = 0;
+ break;
+ }
+
+ bytes -= nextBlockSize;
+ if (buffers.count() == 1) {
+ if (buffers.at(0).size() != basicBlockSize)
+ buffers[0].resize(basicBlockSize);
+ head = tail = 0;
+ tailBuffer = 0;
+ break;
+ }
+
+ buffers.removeAt(0);
+ --tailBuffer;
+ head = 0;
+ }
+
+ if (isEmpty())
+ clear(); // try to minify/squeeze us
+ }
+
+ inline char *reserve(int bytes) {
+ // if this is a fresh empty QRingBuffer
+ if (bufferSize == 0) {
+ buffers[0].resize(qMax(basicBlockSize, bytes));
+ bufferSize += bytes;
+ tail = bytes;
+ return buffers[tailBuffer].data();
+ }
+
+ bufferSize += bytes;
+
+ // if there is already enough space, simply return.
+ if (tail + bytes <= buffers.at(tailBuffer).size()) {
+ char *writePtr = buffers[tailBuffer].data() + tail;
+ tail += bytes;
+ return writePtr;
+ }
+
+ // if our buffer isn't half full yet, simply resize it.
+ if (tail < buffers.at(tailBuffer).size() / 2) {
+ buffers[tailBuffer].resize(tail + bytes);
+ char *writePtr = buffers[tailBuffer].data() + tail;
+ tail += bytes;
+ return writePtr;
+ }
+
+ // shrink this buffer to its current size
+ buffers[tailBuffer].resize(tail);
+
+ // create a new QByteArray with the right size
+ buffers << QByteArray();
+ ++tailBuffer;
+ buffers[tailBuffer].resize(qMax(basicBlockSize, bytes));
+ tail = bytes;
+ return buffers[tailBuffer].data();
+ }
+
+ inline void truncate(int pos) {
+ if (pos < size())
+ chop(size() - pos);
+ }
+
+ inline void chop(int bytes) {
+ bufferSize -= bytes;
+ if (bufferSize < 0)
+ bufferSize = 0;
+
+ for (;;) {
+ // special case: head and tail are in the same buffer
+ if (tailBuffer == 0) {
+ tail -= bytes;
+ if (tail <= head)
+ tail = head = 0;
+ return;
+ }
+
+ if (bytes <= tail) {
+ tail -= bytes;
+ return;
+ }
+
+ bytes -= tail;
+ buffers.removeAt(tailBuffer);
+
+ --tailBuffer;
+ tail = buffers.at(tailBuffer).size();
+ }
+
+ if (isEmpty())
+ clear(); // try to minify/squeeze us
+ }
+
+ inline bool isEmpty() const {
+ return tailBuffer == 0 && tail == 0;
+ }
+
+ inline int getChar() {
+ if (isEmpty())
+ return -1;
+ char c = *readPointer();
+ free(1);
+ return int(uchar(c));
+ }
+
+ inline void putChar(char c) {
+ char *ptr = reserve(1);
+ *ptr = c;
+ }
+
+ inline void ungetChar(char c) {
+ --head;
+ if (head < 0) {
+ buffers.prepend(QByteArray());
+ buffers[0].resize(basicBlockSize);
+ head = basicBlockSize - 1;
+ ++tailBuffer;
+ }
+ buffers[0][head] = c;
+ ++bufferSize;
+ }
+
+ inline int size() const {
+ return bufferSize;
+ }
+
+ inline void clear() {
+ buffers.erase(buffers.begin() + 1, buffers.end());
+ buffers[0].resize(0);
+ buffers[0].squeeze();
+
+ head = tail = 0;
+ tailBuffer = 0;
+ bufferSize = 0;
+ }
+
+ inline int indexOf(char c) const {
+ int index = 0;
+ for (int i = 0; i < buffers.size(); ++i) {
+ int start = 0;
+ int end = buffers.at(i).size();
+
+ if (i == 0)
+ start = head;
+ if (i == tailBuffer)
+ end = tail;
+ const char *ptr = buffers.at(i).data() + start;
+ for (int j = start; j < end; ++j) {
+ if (*ptr++ == c)
+ return index;
+ ++index;
+ }
+ }
+ return -1;
+ }
+
+ inline int indexOf(char c, int maxLength) const {
+ int index = 0;
+ int remain = qMin(size(), maxLength);
+ for (int i = 0; remain && i < buffers.size(); ++i) {
+ int start = 0;
+ int end = buffers.at(i).size();
+
+ if (i == 0)
+ start = head;
+ if (i == tailBuffer)
+ end = tail;
+ if (remain < end - start) {
+ end = start + remain;
+ remain = 0;
+ } else {
+ remain -= end - start;
+ }
+ const char *ptr = buffers.at(i).data() + start;
+ for (int j = start; j < end; ++j) {
+ if (*ptr++ == c)
+ return index;
+ ++index;
+ }
+ }
+ return -1;
+ }
+
+ inline int read(char *data, int maxLength) {
+ int bytesToRead = qMin(size(), maxLength);
+ int readSoFar = 0;
+ while (readSoFar < bytesToRead) {
+ const char *ptr = readPointer();
+ int bytesToReadFromThisBlock = qMin(bytesToRead - readSoFar, nextDataBlockSize());
+ if (data)
+ memcpy(data + readSoFar, ptr, bytesToReadFromThisBlock);
+ readSoFar += bytesToReadFromThisBlock;
+ free(bytesToReadFromThisBlock);
+ }
+ return readSoFar;
+ }
+
+ inline QByteArray read(int maxLength) {
+ QByteArray tmp;
+ tmp.resize(qMin(maxLength, size()));
+ read(tmp.data(), tmp.size());
+ return tmp;
+ }
+
+ inline QByteArray readAll() {
+ return read(size());
+ }
+
+ // read an unspecified amount (will read the first buffer)
+ inline QByteArray read() {
+ if (bufferSize == 0)
+ return QByteArray();
+
+ // multiple buffers, just take the first one
+ if (head == 0 && tailBuffer != 0) {
+ QByteArray qba = buffers.takeFirst();
+ --tailBuffer;
+ bufferSize -= qba.length();
+ return qba;
+ }
+
+ // one buffer with good value for head. Just take it.
+ if (head == 0 && tailBuffer == 0) {
+ QByteArray qba = buffers.takeFirst();
+ qba.resize(tail);
+ buffers << QByteArray();
+ bufferSize = 0;
+ tail = 0;
+ return qba;
+ }
+
+ // Bad case: We have to memcpy.
+ // We can avoid by initializing the QRingBuffer with basicBlockSize of 0
+ // and only using this read() function.
+ QByteArray qba(readPointer(), nextDataBlockSize());
+ buffers.removeFirst();
+ head = 0;
+ if (tailBuffer == 0) {
+ buffers << QByteArray();
+ tail = 0;
+ } else {
+ --tailBuffer;
+ }
+ bufferSize -= qba.length();
+ return qba;
+ }
+
+ // append a new buffer to the end
+ inline void append(const QByteArray &qba) {
+ buffers[tailBuffer].resize(tail);
+ buffers << qba;
+ ++tailBuffer;
+ tail = qba.length();
+ bufferSize += qba.length();
+ }
+
+ inline QByteArray peek(int maxLength) const {
+ int bytesToRead = qMin(size(), maxLength);
+ if(maxLength <= 0)
+ return QByteArray();
+ QByteArray ret;
+ ret.resize(bytesToRead);
+ int readSoFar = 0;
+ for (int i = 0; readSoFar < bytesToRead && i < buffers.size(); ++i) {
+ int start = 0;
+ int end = buffers.at(i).size();
+ if (i == 0)
+ start = head;
+ if (i == tailBuffer)
+ end = tail;
+ const int len = qMin(ret.size()-readSoFar, end-start);
+ memcpy(ret.data()+readSoFar, buffers.at(i).constData()+start, len);
+ readSoFar += len;
+ }
+ Q_ASSERT(readSoFar == ret.size());
+ return ret;
+ }
+
+ inline int skip(int length) {
+ return read(0, length);
+ }
+
+ inline int readLine(char *data, int maxLength) {
+ int index = indexOf('\n');
+ if (index == -1)
+ return read(data, maxLength);
+ if (maxLength <= 0)
+ return -1;
+
+ int readSoFar = 0;
+ while (readSoFar < index + 1 && readSoFar < maxLength - 1) {
+ int bytesToRead = qMin((index + 1) - readSoFar, nextDataBlockSize());
+ bytesToRead = qMin(bytesToRead, (maxLength - 1) - readSoFar);
+ memcpy(data + readSoFar, readPointer(), bytesToRead);
+ readSoFar += bytesToRead;
+ free(bytesToRead);
+ }
+
+ // Terminate it.
+ data[readSoFar] = '\0';
+ return readSoFar;
+ }
+
+ inline bool canReadLine() const {
+ return indexOf('\n') != -1;
+ }
+
+private:
+ QList<QByteArray> buffers;
+ int head, tail;
+ int tailBuffer; // always buffers.size() - 1
+ int basicBlockSize;
+ int bufferSize;
+};
+
+QT_END_NAMESPACE
+
+#endif // QRINGBUFFER_P_H