summaryrefslogtreecommitdiffstats
path: root/src/qt3support/network
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/qt3support/network
Long live Qt 4.5!
Diffstat (limited to 'src/qt3support/network')
-rw-r--r--src/qt3support/network/network.pri30
-rw-r--r--src/qt3support/network/q3dns.cpp2620
-rw-r--r--src/qt3support/network/q3dns.h174
-rw-r--r--src/qt3support/network/q3ftp.cpp2378
-rw-r--r--src/qt3support/network/q3ftp.h204
-rw-r--r--src/qt3support/network/q3http.cpp2322
-rw-r--r--src/qt3support/network/q3http.h278
-rw-r--r--src/qt3support/network/q3localfs.cpp404
-rw-r--r--src/qt3support/network/q3localfs.h84
-rw-r--r--src/qt3support/network/q3network.cpp73
-rw-r--r--src/qt3support/network/q3network.h63
-rw-r--r--src/qt3support/network/q3networkprotocol.cpp1209
-rw-r--r--src/qt3support/network/q3networkprotocol.h250
-rw-r--r--src/qt3support/network/q3serversocket.cpp298
-rw-r--r--src/qt3support/network/q3serversocket.h94
-rw-r--r--src/qt3support/network/q3socket.cpp1518
-rw-r--r--src/qt3support/network/q3socket.h157
-rw-r--r--src/qt3support/network/q3socketdevice.cpp757
-rw-r--r--src/qt3support/network/q3socketdevice.h177
-rw-r--r--src/qt3support/network/q3socketdevice_unix.cpp926
-rw-r--r--src/qt3support/network/q3socketdevice_win.cpp1068
-rw-r--r--src/qt3support/network/q3url.cpp1319
-rw-r--r--src/qt3support/network/q3url.h139
-rw-r--r--src/qt3support/network/q3urloperator.cpp1212
-rw-r--r--src/qt3support/network/q3urloperator.h138
25 files changed, 17892 insertions, 0 deletions
diff --git a/src/qt3support/network/network.pri b/src/qt3support/network/network.pri
new file mode 100644
index 0000000000..31ea682ef5
--- /dev/null
+++ b/src/qt3support/network/network.pri
@@ -0,0 +1,30 @@
+# Qt compat module
+
+HEADERS += network/q3dns.h \
+ network/q3ftp.h \
+ network/q3http.h \
+ network/q3localfs.h \
+ network/q3network.h \
+ network/q3networkprotocol.h \
+ network/q3socket.h \
+ network/q3socketdevice.h \
+ network/q3serversocket.h \
+ network/q3url.h \
+ network/q3urloperator.h
+
+SOURCES += network/q3dns.cpp \
+ network/q3ftp.cpp \
+ network/q3http.cpp \
+ network/q3localfs.cpp \
+ network/q3network.cpp \
+ network/q3networkprotocol.cpp \
+ network/q3socket.cpp \
+ network/q3socketdevice.cpp \
+ network/q3serversocket.cpp \
+ network/q3url.cpp \
+ network/q3urloperator.cpp
+
+win32:SOURCES += network/q3socketdevice_win.cpp
+unix:SOURCES += network/q3socketdevice_unix.cpp
+mac:LIBS += -lresolv
+
diff --git a/src/qt3support/network/q3dns.cpp b/src/qt3support/network/q3dns.cpp
new file mode 100644
index 0000000000..c53f2ff3e3
--- /dev/null
+++ b/src/qt3support/network/q3dns.cpp
@@ -0,0 +1,2620 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "qbytearray.h"
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_CYGWIN)
+# include "qt_windows.h"
+#else
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+extern "C" int res_init();
+#endif
+
+// POSIX Large File Support redefines open -> open64
+#if defined(open)
+# undef open
+#endif
+
+// POSIX Large File Support redefines truncate -> truncate64
+#if defined(truncate)
+# undef truncate
+#endif
+
+// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
+#if defined(connect)
+# undef connect
+#endif
+
+// UnixWare 7 redefines socket -> _socket
+#if defined(socket)
+# undef socket
+#endif
+
+#include "q3dns.h"
+
+#ifndef QT_NO_DNS
+
+#include "qdatetime.h"
+#include "q3dict.h"
+#include "q3ptrlist.h"
+#include "qstring.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "q3ptrvector.h"
+#include "q3strlist.h"
+#include "q3ptrdict.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "q3socketdevice.h"
+#include "q3cleanuphandler.h"
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3DNS_DEBUG
+
+static Q_UINT16 theId; // ### seeded started by now()
+
+
+static QDateTime * originOfTime = 0;
+
+static Q3CleanupHandler<QDateTime> q3dns_cleanup_time;
+
+static Q_UINT32 now()
+{
+ if ( originOfTime )
+ return originOfTime->secsTo( QDateTime::currentDateTime() );
+
+ originOfTime = new QDateTime( QDateTime::currentDateTime() );
+ theId = originOfTime->time().msec() * 60 + originOfTime->time().second();
+ q3dns_cleanup_time.add( &originOfTime );
+ return 0;
+}
+
+
+static Q3PtrList<QHostAddress> * theNs = 0;
+static Q3StrList * theDomains = 0;
+static bool ipv6support = false;
+
+class Q3DnsPrivate {
+public:
+ Q3DnsPrivate() : queryTimer( 0 ), noNames(false)
+ {
+#if defined(Q_DNS_SYNCHRONOUS)
+#if defined(Q_OS_UNIX)
+ noEventLoop = qApp==0 || qApp->loopLevel()==0;
+#else
+ noEventLoop = false;
+#endif
+#endif
+ }
+ ~Q3DnsPrivate()
+ {
+ delete queryTimer;
+ }
+private:
+ QTimer * queryTimer;
+ bool noNames;
+#if defined(Q_DNS_SYNCHRONOUS)
+ bool noEventLoop;
+#endif
+
+ friend class Q3Dns;
+ friend class Q3DnsAnswer;
+};
+
+
+class Q3DnsRR;
+class Q3DnsDomain;
+
+
+
+// Q3DnsRR is the class used to store a single RR. Q3DnsRR can store
+// all of the supported RR types. a Q3DnsRR is always cached.
+
+// Q3DnsRR is mostly constructed from the outside. a but hacky, but
+// permissible since the entire class is internal.
+
+class Q3DnsRR {
+public:
+ Q3DnsRR( const QString & label );
+ ~Q3DnsRR();
+
+public:
+ Q3DnsDomain * domain;
+ Q3Dns::RecordType t;
+ bool nxdomain;
+ bool current;
+ Q_UINT32 expireTime;
+ Q_UINT32 deleteTime;
+ // somewhat space-wasting per-type data
+ // a / aaaa
+ QHostAddress address;
+ // cname / mx / srv / ptr
+ QString target;
+ // mx / srv
+ Q_UINT16 priority;
+ // srv
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ // txt
+ QString text; // could be overloaded into target...
+private:
+
+};
+
+
+class Q3DnsDomain {
+public:
+ Q3DnsDomain( const QString & label );
+ ~Q3DnsDomain();
+
+ static void add( const QString & label, Q3DnsRR * );
+ static Q3PtrList<Q3DnsRR> * cached( const Q3Dns * );
+
+ void take( Q3DnsRR * );
+
+ void sweep( Q_UINT32 thisSweep );
+
+ bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
+
+ QString name() const { return l; }
+
+public:
+ QString l;
+ Q3PtrList<Q3DnsRR> * rrs;
+};
+
+
+class Q3DnsQuery: public QTimer { // this inheritance is a very evil hack
+public:
+ Q3DnsQuery():
+ id( 0 ), t( Q3Dns::None ), step(0), started(0),
+ dns( new Q3PtrDict<void>(17) ) {}
+ ~Q3DnsQuery() { delete dns; }
+ Q_UINT16 id;
+ Q3Dns::RecordType t;
+ QString l;
+
+ uint step;
+ Q_UINT32 started;
+
+ Q3PtrDict<void> * dns;
+};
+
+
+
+class Q3DnsAnswer {
+public:
+ Q3DnsAnswer( Q3DnsQuery * );
+ Q3DnsAnswer( const QByteArray &, Q3DnsQuery * );
+ ~Q3DnsAnswer();
+
+ void parse();
+ void notify();
+
+ bool ok;
+
+private:
+ Q3DnsQuery * query;
+
+ Q_UINT8 * answer;
+ int size;
+ int pp;
+
+ Q3PtrList<Q3DnsRR> * rrs;
+
+ // convenience
+ int next;
+ int ttl;
+ QString label;
+ Q3DnsRR * rr;
+
+ QString readString(bool multipleLabels = true);
+ void parseA();
+ void parseAaaa();
+ void parseMx();
+ void parseSrv();
+ void parseCname();
+ void parsePtr();
+ void parseTxt();
+ void parseNs();
+};
+
+
+Q3DnsRR::Q3DnsRR( const QString & label )
+ : domain( 0 ), t( Q3Dns::None ),
+ nxdomain( false ), current( false ),
+ expireTime( 0 ), deleteTime( 0 ),
+ priority( 0 ), weight( 0 ), port( 0 )
+{
+ Q3DnsDomain::add( label, this );
+}
+
+
+// not supposed to be deleted except by Q3DnsDomain
+Q3DnsRR::~Q3DnsRR()
+{
+ // nothing is necessary
+}
+
+
+// this one just sticks in a NXDomain
+Q3DnsAnswer::Q3DnsAnswer( Q3DnsQuery * query_ )
+{
+ ok = true;
+
+ answer = 0;
+ size = 0;
+ query = query_;
+ pp = 0;
+ rrs = new Q3PtrList<Q3DnsRR>;
+ rrs->setAutoDelete( false );
+ next = size;
+ ttl = 0;
+ label.clear();
+ rr = 0;
+
+ Q3DnsRR * newrr = new Q3DnsRR( query->l );
+ newrr->t = query->t;
+ newrr->deleteTime = query->started + 10;
+ newrr->expireTime = query->started + 10;
+ newrr->nxdomain = true;
+ newrr->current = true;
+ rrs->append( newrr );
+}
+
+
+Q3DnsAnswer::Q3DnsAnswer( const QByteArray& answer_,
+ Q3DnsQuery * query_ )
+{
+ ok = true;
+
+ answer = (Q_UINT8 *)(answer_.data());
+ size = (int)answer_.size();
+ query = query_;
+ pp = 0;
+ rrs = new Q3PtrList<Q3DnsRR>;
+ rrs->setAutoDelete( false );
+ next = size;
+ ttl = 0;
+ label.clear();
+ rr = 0;
+}
+
+
+Q3DnsAnswer::~Q3DnsAnswer()
+{
+ if ( !ok && rrs ) {
+ Q3PtrListIterator<Q3DnsRR> it( *rrs );
+ Q3DnsRR * tmprr;
+ while( (tmprr=it.current()) != 0 ) {
+ ++it;
+ tmprr->t = Q3Dns::None; // will be deleted soonish
+ }
+ }
+ delete rrs;
+}
+
+
+QString Q3DnsAnswer::readString(bool multipleLabels)
+{
+ int p = pp;
+ QString r;
+ Q_UINT8 b;
+ for( ;; ) {
+ b = 128;
+ // Read one character
+ if ( p >= 0 && p < size )
+ b = answer[p];
+
+ switch( b >> 6 ) {
+ case 0:
+ // b is less than 64
+ p++;
+
+ // Detect end of data
+ if ( b == 0 ) {
+ if ( p > pp )
+ pp = p;
+ return r.isNull() ? QLatin1String( "." ) : r;
+ }
+
+ // Read a label of size 'b' characters
+ if ( !r.isNull() )
+ r += QLatin1Char('.');
+ while( b-- > 0 )
+ r += QLatin1Char( answer[p++] );
+
+ // Return immediately if we were only supposed to read one
+ // label.
+ if (!multipleLabels)
+ return r;
+
+ break;
+ default:
+ // Ignore unrecognized control character, or p was out of
+ // range.
+ goto not_ok;
+ case 3:
+ // Use the next character to determine the relative offset
+ // to jump to before continuing the packet parsing.
+ int q = ( (answer[p] & 0x3f) << 8 ) + answer[p+1];
+
+ if ( q >= pp || q >= p )
+ goto not_ok;
+ if ( p >= pp )
+ pp = p + 2;
+ p = q;
+ }
+ }
+not_ok:
+ ok = false;
+ return QString();
+}
+
+
+
+void Q3DnsAnswer::parseA()
+{
+ if ( next != pp + 4 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN A for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::A;
+ rr->address = QHostAddress( ( answer[pp+0] << 24 ) +
+ ( answer[pp+1] << 16 ) +
+ ( answer[pp+2] << 8 ) +
+ ( answer[pp+3] ) );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN A %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseAaaa()
+{
+ if ( next != pp + 16 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN Aaaa for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Aaaa;
+ rr->address = QHostAddress( answer+pp );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+
+void Q3DnsAnswer::parseMx()
+{
+ if ( next < pp + 2 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN MX for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ pp += 2;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad string in MX for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = Q3Dns::Mx;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseSrv()
+{
+ if ( next < pp + 6 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %d bytes long IN SRV for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ rr->weight = (answer[pp+2] << 8) + answer[pp+3];
+ rr->port = (answer[pp+4] << 8) + answer[pp+5];
+ pp += 6;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad string in SRV for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = Q3Dns::Srv;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseCname()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Cname;
+ rr->target = target;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseNs()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ // parse, but ignore
+
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN NS %s (ttl %d)", label.ascii(),
+ target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parsePtr()
+{
+ QString target = readString().lower();
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad PTR for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Ptr;
+ rr->target = target;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN PTR %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parseTxt()
+{
+ QString text = readString(false);
+ if ( !ok ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw bad TXT for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new Q3DnsRR( label );
+ rr->t = Q3Dns::Txt;
+ rr->text = text;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
+ rr->text.ascii(), ttl );
+#endif
+}
+
+
+void Q3DnsAnswer::parse()
+{
+ // okay, do the work...
+ if ( (answer[2] & 0x78) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: answer to wrong query type (%d)", answer[1] );
+#endif
+ ok = false;
+ return;
+ }
+
+ // AA
+ bool aa = (answer[2] & 4) != 0;
+
+ // TC
+ if ( (answer[2] & 2) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: truncated answer; pressing on" );
+#endif
+ }
+
+ // RD
+ bool rd = (answer[2] & 1) != 0;
+
+ // we don't test RA
+ // we don't test the MBZ fields
+
+ if ( (answer[3] & 0x0f) == 3 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
+#endif
+ // NXDomain. cache that for one minute.
+ rr = new Q3DnsRR( query->l );
+ rr->t = query->t;
+ rr->deleteTime = query->started + 60;
+ rr->expireTime = query->started + 60;
+ rr->nxdomain = true;
+ rr->current = true;
+ rrs->append( rr );
+ return;
+ }
+
+ if ( (answer[3] & 0x0f) != 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: error code %d", answer[3] & 0x0f );
+#endif
+ ok = false;
+ return;
+ }
+
+ int qdcount = ( answer[4] << 8 ) + answer[5];
+ int ancount = ( answer[6] << 8 ) + answer[7];
+ int nscount = ( answer[8] << 8 ) + answer[9];
+ int adcount = (answer[10] << 8 ) +answer[11];
+
+ pp = 12;
+
+ // read query
+ while( qdcount > 0 && pp < size ) {
+ // should I compare the string against query->l?
+ (void)readString();
+ if ( !ok )
+ return;
+ pp += 4;
+ qdcount--;
+ }
+
+ // answers and stuff
+ int rrno = 0;
+ // if we parse the answer completely, but there are no answers,
+ // ignore the entire thing.
+ int answers = 0;
+ while( ( rrno < ancount ||
+ ( ok && answers >0 && rrno < ancount + nscount + adcount ) ) &&
+ pp < size ) {
+ label = readString().lower();
+ if ( !ok )
+ return;
+ int rdlength = 0;
+ if ( pp + 10 <= size )
+ rdlength = ( answer[pp+8] << 8 ) + answer[pp+9];
+ if ( pp + 10 + rdlength > size ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: ran out of stuff to parse (%d+%d>%d (%d)",
+ pp, rdlength, size, rrno < ancount );
+#endif
+ // if we're still in the AN section, we should go back and
+ // at least down the TTLs. probably best to invalidate
+ // the results.
+ // the rrs list is good for this
+ ok = ( rrno < ancount );
+ return;
+ }
+ uint type, clas;
+ type = ( answer[pp+0] << 8 ) + answer[pp+1];
+ clas = ( answer[pp+2] << 8 ) + answer[pp+3];
+ ttl = ( answer[pp+4] << 24 ) + ( answer[pp+5] << 16 ) +
+ ( answer[pp+6] << 8 ) + answer[pp+7];
+ pp = pp + 10;
+ if ( clas != 1 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: class %d (not internet) for %s",
+ clas, label.isNull() ? "." : label.ascii() );
+#endif
+ } else {
+ next = pp + rdlength;
+ rr = 0;
+ switch( type ) {
+ case 1:
+ parseA();
+ break;
+ case 28:
+ parseAaaa();
+ break;
+ case 15:
+ parseMx();
+ break;
+ case 33:
+ parseSrv();
+ break;
+ case 5:
+ parseCname();
+ break;
+ case 12:
+ parsePtr();
+ break;
+ case 16:
+ parseTxt();
+ break;
+ case 2:
+ parseNs();
+ break;
+ default:
+ // something we don't know
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: type %d for %s", type,
+ label.isNull() ? "." : label.ascii() );
+#endif
+ break;
+ }
+ if ( rr ) {
+ rr->deleteTime = 0;
+ if ( ttl > 0 )
+ rr->expireTime = query->started + ttl;
+ else
+ rr->expireTime = query->started + 20;
+ if ( rrno < ancount ) {
+ answers++;
+ rr->deleteTime = rr->expireTime;
+ }
+ rr->current = true;
+ rrs->append( rr );
+ }
+ }
+ if ( !ok )
+ return;
+ pp = next;
+ next = size;
+ rrno++;
+ }
+ if ( answers == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: answer contained no answers" );
+#endif
+ ok = ( aa && rd );
+ }
+
+ // now go through the list and mark all the As that are referenced
+ // by something we care about. we want to cache such As.
+ rrs->first();
+ Q3Dict<void> used( 17 );
+ used.setAutoDelete( false );
+ while( (rr=rrs->current()) != 0 ) {
+ rrs->next();
+ if ( rr->target.length() && rr->deleteTime > 0 && rr->current )
+ used.insert( rr->target, (void*)42 );
+ if ( ( rr->t == Q3Dns::A || rr->t == Q3Dns::Aaaa ) &&
+ used.find( rr->domain->name() ) != 0 )
+ rr->deleteTime = rr->expireTime;
+ }
+
+ // next, for each RR, delete any older RRs that are equal to it
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ rrs->next();
+ if ( rr && rr->domain && rr->domain->rrs ) {
+ Q3PtrList<Q3DnsRR> * drrs = rr->domain->rrs;
+ drrs->first();
+ Q3DnsRR * older;
+ while( (older=drrs->current()) != 0 ) {
+ if ( older != rr &&
+ older->t == rr->t &&
+ older->nxdomain == rr->nxdomain &&
+ older->address == rr->address &&
+ older->target == rr->target &&
+ older->priority == rr->priority &&
+ older->weight == rr->weight &&
+ older->port == rr->port &&
+ older->text == rr->text ) {
+ // well, it's equal, but it's not the same. so we kill it,
+ // but use its expiry time.
+#if defined(Q3DNS_DEBUG)
+ qDebug( "killing off old %d for %s, expire was %d",
+ older->t, older->domain->name().latin1(),
+ rr->expireTime );
+#endif
+ older->t = Q3Dns::None;
+ rr->expireTime = QMAX( older->expireTime, rr->expireTime );
+ rr->deleteTime = QMAX( older->deleteTime, rr->deleteTime );
+ older->deleteTime = 0;
+#if defined(Q3DNS_DEBUG)
+ qDebug( " adjusted expire is %d", rr->expireTime );
+#endif
+ }
+ drrs->next();
+ }
+ }
+ }
+
+#if defined(Q3DNS_DEBUG)
+ //qDebug( "DNS Manager: ()" );
+#endif
+}
+
+
+class Q3DnsUgleHack: public Q3Dns {
+public:
+ void ugle( bool emitAnyway=false );
+};
+
+
+void Q3DnsAnswer::notify()
+{
+ if ( !rrs || !ok || !query || !query->dns )
+ return;
+
+ Q3PtrDict<void> notified;
+ notified.setAutoDelete( false );
+
+ Q3PtrDictIterator<void> it( *query->dns );
+ Q3Dns * dns;
+ it.toFirst();
+ while( (dns=(Q3Dns*)(it.current())) != 0 ) {
+ ++it;
+ if ( notified.find( (void*)dns ) == 0 ) {
+ notified.insert( (void*)dns, (void*)42 );
+ if ( rrs->count() == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: found no answers!" );
+#endif
+ dns->d->noNames = true;
+ ((Q3DnsUgleHack*)dns)->ugle( true );
+ } else {
+ QStringList n = dns->qualifiedNames();
+ if ( query && n.contains(query->l) )
+ ((Q3DnsUgleHack*)dns)->ugle();
+#if defined(Q3DNS_DEBUG)
+ else
+ qDebug( "DNS Manager: DNS thing %s not notified for %s",
+ dns->label().ascii(), query->l.ascii() );
+#endif
+ }
+ }
+ }
+}
+
+
+//
+//
+// Q3DnsManager
+//
+//
+
+
+class Q3DnsManager: public Q3DnsSocket {
+private:
+public: // just to silence the moronic g++.
+ Q3DnsManager();
+ ~Q3DnsManager();
+public:
+ static Q3DnsManager * manager();
+
+ Q3DnsDomain * domain( const QString & );
+
+ void transmitQuery( Q3DnsQuery * );
+ void transmitQuery( int );
+
+ // reimplementation of the slots
+ void cleanCache();
+ void retransmit();
+ void answer();
+
+public:
+ Q3PtrVector<Q3DnsQuery> queries;
+ Q3Dict<Q3DnsDomain> cache;
+ Q3SocketDevice * ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ Q3SocketDevice * ipv6Socket;
+#endif
+};
+
+
+
+static Q3DnsManager * globalManager = 0;
+
+static void cleanupDns()
+{
+ delete globalManager;
+ globalManager = 0;
+}
+
+Q3DnsManager * Q3DnsManager::manager()
+{
+ if ( !globalManager ) {
+ qAddPostRoutine(cleanupDns);
+ new Q3DnsManager();
+ }
+ return globalManager;
+}
+
+
+void Q3DnsUgleHack::ugle( bool emitAnyway)
+{
+ if ( emitAnyway || !isWorking() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: status change for %s (type %d)",
+ label().ascii(), recordType() );
+#endif
+ emit resultsReady();
+ }
+}
+
+
+Q3DnsManager::Q3DnsManager()
+ : Q3DnsSocket( qApp, "Internal DNS manager" ),
+ queries( Q3PtrVector<Q3DnsQuery>( 0 ) ),
+ cache( Q3Dict<Q3DnsDomain>( 83, false ) ),
+ ipv4Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv4, 0 ) )
+#if !defined (QT_NO_IPV6)
+ , ipv6Socket( new Q3SocketDevice( Q3SocketDevice::Datagram, Q3SocketDevice::IPv6, 0 ) )
+#endif
+{
+ cache.setAutoDelete( true );
+ globalManager = this;
+
+ QTimer * sweepTimer = new QTimer( this );
+ sweepTimer->start( 1000 * 60 * 3 );
+ connect( sweepTimer, SIGNAL(timeout()),
+ this, SLOT(cleanCache()) );
+
+ QSocketNotifier * rn4 = new QSocketNotifier( ipv4Socket->socket(),
+ QSocketNotifier::Read,
+ this, "dns IPv4 socket watcher" );
+ ipv4Socket->setAddressReusable( false );
+ ipv4Socket->setBlocking( false );
+ connect( rn4, SIGNAL(activated(int)), SLOT(answer()) );
+
+#if !defined (QT_NO_IPV6)
+ // Don't connect the IPv6 socket notifier if the host does not
+ // support IPv6.
+ if ( ipv6Socket->socket() != -1 ) {
+ QSocketNotifier * rn6 = new QSocketNotifier( ipv6Socket->socket(),
+ QSocketNotifier::Read,
+ this, "dns IPv6 socket watcher" );
+
+ ipv6support = true;
+ ipv6Socket->setAddressReusable( false );
+ ipv6Socket->setBlocking( false );
+ connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
+ }
+#endif
+
+ if ( !theNs )
+ Q3Dns::doResInit();
+
+ // O(n*n) stuff here. but for 3 and 6, O(n*n) with a low k should
+ // be perfect. the point is to eliminate any duplicates that
+ // might be hidden in the lists.
+ Q3PtrList<QHostAddress> * ns = new Q3PtrList<QHostAddress>;
+
+ theNs->first();
+ QHostAddress * h;
+ while( (h=theNs->current()) != 0 ) {
+ ns->first();
+ while( ns->current() != 0 && !(*ns->current() == *h) )
+ ns->next();
+ if ( !ns->current() ) {
+ ns->append( new QHostAddress(*h) );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "using name server %s", h->toString().latin1() );
+ } else {
+ qDebug( "skipping address %s", h->toString().latin1() );
+#endif
+ }
+ theNs->next();
+ }
+
+ delete theNs;
+ theNs = ns;
+ theNs->setAutoDelete( true );
+
+ Q3StrList * domains = new Q3StrList( true );
+
+ theDomains->first();
+ const char * s;
+ while( (s=theDomains->current()) != 0 ) {
+ domains->first();
+ while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
+ domains->next();
+ if ( !domains->current() ) {
+ domains->append( s );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "searching domain %s", s );
+ } else {
+ qDebug( "skipping domain %s", s );
+#endif
+ }
+ theDomains->next();
+ }
+
+ delete theDomains;
+ theDomains = domains;
+ theDomains->setAutoDelete( true );
+}
+
+
+Q3DnsManager::~Q3DnsManager()
+{
+ if ( globalManager )
+ globalManager = 0;
+ queries.setAutoDelete( true );
+ cache.setAutoDelete( true );
+ delete ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ delete ipv6Socket;
+#endif
+}
+
+static Q_UINT32 lastSweep = 0;
+
+void Q3DnsManager::cleanCache()
+{
+ bool again = false;
+ Q3DictIterator<Q3DnsDomain> it( cache );
+ Q3DnsDomain * d;
+ Q_UINT32 thisSweep = now();
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3DnsManager::cleanCache(: Called, time is %u, last was %u",
+ thisSweep, lastSweep );
+#endif
+
+ while( (d=it.current()) != 0 ) {
+ ++it;
+ d->sweep( thisSweep ); // after this, d may be empty
+ if ( !again )
+ again = !d->isEmpty();
+ }
+ if ( !again )
+ delete this;
+ lastSweep = thisSweep;
+}
+
+
+void Q3DnsManager::retransmit()
+{
+ const QObject * o = sender();
+ if ( o == 0 || globalManager == 0 || this != globalManager )
+ return;
+ uint q = 0;
+ while( q < queries.size() && queries[q] != o )
+ q++;
+ if ( q < queries.size() )
+ transmitQuery( q );
+}
+
+
+void Q3DnsManager::answer()
+{
+ QByteArray a( 16383 ); // large enough for anything, one suspects
+
+ int r;
+#if defined (QT_NO_IPV6)
+ r = ipv4Socket->readBlock(a.data(), a.size());
+#else
+ if (((QSocketNotifier *)sender())->socket() == ipv4Socket->socket())
+ r = ipv4Socket->readBlock(a.data(), a.size());
+ else
+ r = ipv6Socket->readBlock(a.data(), a.size());
+#endif
+#if defined(Q3DNS_DEBUG)
+#if !defined (QT_NO_IPV6)
+ qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
+ useIpv4Socket ? ipv4Socket->peerAddress().toString().ascii()
+ : ipv6Socket->peerAddress().toString().ascii(),
+ useIpv4Socket ? ipv4Socket->peerPort() : ipv6Socket->peerPort() );
+#else
+ qDebug("DNS Manager: answer arrived: %d bytes from %s:%d", r,
+ ipv4Socket->peerAddress().toString().ascii(), ipv4Socket->peerPort());;
+#endif
+#endif
+ if ( r < 12 )
+ return;
+ // maybe we should check that the answer comes from port 53 on one
+ // of our name servers...
+ a.resize( r );
+
+ Q_UINT16 aid = (((Q_UINT8)a[0]) << 8) + ((Q_UINT8)a[1]);
+ uint i = 0;
+ while( i < queries.size() &&
+ !( queries[i] && queries[i]->id == aid ) )
+ i++;
+ if ( i == queries.size() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: bad id (0x%04x) %d", aid, i );
+#endif
+ return;
+ }
+
+ // at this point queries[i] is whatever we asked for.
+
+ if ( ( (Q_UINT8)(a[2]) & 0x80 ) == 0 ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: received a query" );
+#endif
+ return;
+ }
+
+ Q3DnsQuery * q = queries[i];
+ Q3DnsAnswer answer( a, q );
+ answer.parse();
+ if ( answer.ok ) {
+ queries.take( i );
+ answer.notify();
+ delete q;
+ }
+}
+
+
+void Q3DnsManager::transmitQuery( Q3DnsQuery * query_ )
+{
+ if ( !query_ )
+ return;
+
+ uint i = 0;
+ while( i < queries.size() && queries[i] != 0 )
+ i++;
+ if ( i == queries.size() )
+ queries.resize( i+1 );
+ queries.insert( i, query_ );
+ transmitQuery( i );
+}
+
+
+void Q3DnsManager::transmitQuery( int i )
+{
+ if ( i < 0 || i >= (int)queries.size() )
+ return;
+ Q3DnsQuery * q = queries[i];
+
+ if ( q && q->step > 8 ) {
+ // okay, we've run out of retransmissions. we fake an NXDomain
+ // with a very short life time...
+ Q3DnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
+#endif
+ delete q;
+ QTimer::singleShot( 0, Q3DnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ if ((q && !q->dns) || q->dns->isEmpty())
+ // no one currently wants the answer, so there's no point in
+ // retransmitting the query. we keep it, though. an answer may
+ // arrive for an earlier query transmission, and if it does we
+ // may benefit from caching the result.
+ return;
+
+ QByteArray p( 12 + q->l.length() + 2 + 4 );
+ if ( p.size() > 500 )
+ return; // way over the limit, so don't even try
+
+ // header
+ // id
+ p[0] = (q->id & 0xff00) >> 8;
+ p[1] = q->id & 0x00ff;
+ p[2] = 1; // recursion desired, rest is 0
+ p[3] = 0; // all is 0
+ // one query
+ p[4] = 0;
+ p[5] = 1;
+ // no answers, name servers or additional data
+ p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
+
+ // the name is composed of several components. each needs to be
+ // written by itself... so we write...
+ // oh, and we assume that there's no funky characters in there.
+ int pp = 12;
+ uint lp = 0;
+ while( lp < (uint) q->l.length() ) {
+ int le = q->l.find( QLatin1Char('.'), lp );
+ if ( le < 0 )
+ le = q->l.length();
+ QString component = q->l.mid( lp, le-lp );
+ p[pp++] = component.length();
+ int cp;
+ for( cp=0; cp < (int)component.length(); cp++ )
+ p[pp++] = component[cp].latin1();
+ lp = le + 1;
+ }
+ // final null
+ p[pp++] = 0;
+ // query type
+ p[pp++] = 0;
+ switch( q->t ) {
+ case Q3Dns::A:
+ p[pp++] = 1;
+ break;
+ case Q3Dns::Aaaa:
+ p[pp++] = 28;
+ break;
+ case Q3Dns::Mx:
+ p[pp++] = 15;
+ break;
+ case Q3Dns::Srv:
+ p[pp++] = 33;
+ break;
+ case Q3Dns::Cname:
+ p[pp++] = 5;
+ break;
+ case Q3Dns::Ptr:
+ p[pp++] = 12;
+ break;
+ case Q3Dns::Txt:
+ p[pp++] = 16;
+ break;
+ default:
+ p[pp++] = (char)255; // any
+ break;
+ }
+ // query class (always internet)
+ p[pp++] = 0;
+ p[pp++] = 1;
+
+ // if we have no name servers, we should regenerate ns in case
+ // name servers have recently been defined (like on windows,
+ // plugging/unplugging the network cable will change the name
+ // server entries)
+ if ( !theNs || theNs->isEmpty() )
+ Q3Dns::doResInit();
+
+ if ( !theNs || theNs->isEmpty() ) {
+ // we don't find any name servers. We fake an NXDomain
+ // with a very short life time...
+ Q3DnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(Q3DNS_DEBUG)
+ qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
+#endif
+ delete q;
+ QTimer::singleShot( 1000*10, Q3DnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ QHostAddress receiver = *theNs->at( q->step % theNs->count() );
+ if (receiver.isIPv4Address())
+ ipv4Socket->writeBlock( p.data(), pp, receiver, 53 );
+#if !defined (QT_NO_IPV6)
+ else
+ ipv6Socket->writeBlock( p.data(), pp, receiver, 53 );
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "issuing query 0x%04x (%d) about %s type %d to %s",
+ q->id, q->step, q->l.ascii(), q->t,
+ ns->at( q->step % ns->count() )->toString().ascii() );
+#endif
+ if ( theNs->count() > 1 && q->step == 0 && queries.count() == 1 ) {
+ // if it's the first time, and we don't have any other
+ // outstanding queries, send nonrecursive queries to the other
+ // name servers too.
+ p[2] = 0;
+ QHostAddress * server;
+ while( (server=theNs->next()) != 0 ) {
+ if (server->isIPv4Address())
+ ipv4Socket->writeBlock( p.data(), pp, *server, 53 );
+#if !defined (QT_NO_IPV6)
+ else
+ ipv6Socket->writeBlock( p.data(), pp, *server, 53 );
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "copying query to %s", server->toString().ascii() );
+#endif
+ }
+ }
+ q->step++;
+ // some testing indicates that normal dns queries take up to 0.6
+ // seconds. the graph becomes steep around that point, and the
+ // number of errors rises... so it seems good to retry at that
+ // point.
+ q->start( q->step < theNs->count() ? 800 : 1500, true );
+}
+
+
+Q3DnsDomain * Q3DnsManager::domain( const QString & label )
+{
+ Q3DnsDomain * d = cache.find( label );
+ if ( !d ) {
+ d = new Q3DnsDomain( label );
+ cache.insert( label, d );
+ }
+ return d;
+}
+
+
+//
+//
+// the Q3DnsDomain class looks after and coordinates queries for Q3DnsRRs for
+// each domain, and the cached Q3DnsRRs. (A domain, in DNS terminology, is
+// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are
+// all domains.)
+//
+//
+
+
+Q3DnsDomain::Q3DnsDomain( const QString & label )
+{
+ l = label;
+ rrs = 0;
+}
+
+
+Q3DnsDomain::~Q3DnsDomain()
+{
+ delete rrs;
+ rrs = 0;
+}
+
+
+void Q3DnsDomain::add( const QString & label, Q3DnsRR * rr )
+{
+ Q3DnsDomain * d = Q3DnsManager::manager()->domain( label );
+ if ( !d->rrs ) {
+ d->rrs = new Q3PtrList<Q3DnsRR>;
+ d->rrs->setAutoDelete( true );
+ }
+ d->rrs->append( rr );
+ rr->domain = d;
+}
+
+
+Q3PtrList<Q3DnsRR> * Q3DnsDomain::cached( const Q3Dns * r )
+{
+ Q3PtrList<Q3DnsRR> * l = new Q3PtrList<Q3DnsRR>;
+
+ // test at first if you have to start a query at all
+ if ( r->recordType() == Q3Dns::A ) {
+ if ( r->label().lower() == QLatin1String("localhost") ) {
+ // undocumented hack. ipv4-specific. also, may be a memory
+ // leak? not sure. would be better to do this in doResInit(),
+ // anyway.
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ rrTmp->t = Q3Dns::A;
+ rrTmp->address = QHostAddress( 0x7f000001 );
+ rrTmp->current = true;
+ l->append( rrTmp );
+ return l;
+ }
+ QHostAddress tmp;
+ if ( tmp.setAddress( r->label() ) ) {
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ if ( tmp.isIPv4Address() ) {
+ rrTmp->t = Q3Dns::A;
+ rrTmp->address = tmp;
+ rrTmp->current = true;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = true;
+ }
+ return l;
+ }
+ }
+ if ( r->recordType() == Q3Dns::Aaaa ) {
+ QHostAddress tmp;
+ if ( tmp.setAddress(r->label()) ) {
+ Q3DnsRR *rrTmp = new Q3DnsRR( r->label() );
+ if ( tmp.isIPv6Address() ) {
+ rrTmp->t = Q3Dns::Aaaa;
+ rrTmp->address = tmp;
+ rrTmp->current = true;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = true;
+ }
+ return l;
+ }
+ }
+
+ // if you reach this point, you have to do the query
+ Q3DnsManager * m = Q3DnsManager::manager();
+ QStringList n = r->qualifiedNames();
+ bool nxdomain;
+ int cnamecount = 0;
+ int it = 0;
+ while( it < n.count() ) {
+ QString s = n.at(it++);
+ nxdomain = false;
+#if defined(Q3DNS_DEBUG)
+ qDebug( "looking at cache for %s (%s %d)",
+ s.ascii(), r->label().ascii(), r->recordType() );
+#endif
+ Q3DnsDomain * d = m->domain( s );
+#if defined(Q3DNS_DEBUG)
+ qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
+#endif
+ if ( d->rrs )
+ d->rrs->first();
+ Q3DnsRR * rr;
+ bool answer = false;
+ while( d->rrs && (rr=d->rrs->current()) != 0 ) {
+ if ( rr->t == Q3Dns::Cname && r->recordType() != Q3Dns::Cname &&
+ !rr->nxdomain && cnamecount < 16 ) {
+ // cname. if the code is ugly, that may just
+ // possibly be because the concept is.
+#if defined(Q3DNS_DEBUG)
+ qDebug( "found cname from %s to %s",
+ r->label().ascii(), rr->target.ascii() );
+#endif
+ s = rr->target;
+ d = m->domain( s );
+ if ( d->rrs )
+ d->rrs->first();
+ it = n.count();
+ // we've elegantly moved over to whatever the cname
+ // pointed to. well, not elegantly. let's remember
+ // that we've done something, anyway, so we can't be
+ // fooled into an infinte loop as well.
+ cnamecount++;
+ } else {
+ if ( rr->t == r->recordType() ) {
+ if ( rr->nxdomain )
+ nxdomain = true;
+ else
+ answer = true;
+ l->append( rr );
+ if ( rr->deleteTime <= lastSweep ) {
+ // we're returning something that'll be
+ // deleted soon. we assume that if the client
+ // wanted it twice, it'll want it again, so we
+ // ask the name server again right now.
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = rr->t;
+ query->l = rr->domain->name();
+ // note that here, we don't bother about
+ // notification. but we do bother about
+ // timeouts: we make sure to use high timeouts
+ // and few tramsissions.
+ query->step = theNs->count();
+ QObject::connect( query, SIGNAL(timeout()),
+ Q3DnsManager::manager(),
+ SLOT(retransmit()) );
+ Q3DnsManager::manager()->transmitQuery( query );
+ }
+ }
+ d->rrs->next();
+ }
+ }
+ // if we found a positive result, return quickly
+ if ( answer && l->count() ) {
+#if defined(Q3DNS_DEBUG)
+ qDebug( "found %d records for %s",
+ l->count(), r->label().ascii() );
+ l->first();
+ while( l->current() ) {
+ qDebug( " type %d target %s address %s",
+ l->current()->t,
+ l->current()->target.latin1(),
+ l->current()->address.toString().latin1() );
+ l->next();
+ }
+#endif
+ l->first();
+ return l;
+ }
+
+#if defined(Q3DNS_DEBUG)
+ if ( nxdomain )
+ qDebug( "found NXDomain %s", s.ascii() );
+#endif
+
+ if ( !nxdomain ) {
+ // if we didn't, and not a negative result either, perhaps
+ // we need to transmit a query.
+ uint q = 0;
+ while ( q < m->queries.size() &&
+ ( m->queries[q] == 0 ||
+ m->queries[q]->t != r->recordType() ||
+ m->queries[q]->l != s ) )
+ q++;
+ // we haven't done it before, so maybe we should. but
+ // wait - if it's an unqualified name, only ask when all
+ // the other alternatives are exhausted.
+ if ( q == m->queries.size() && ( s.find( QLatin1Char('.') ) >= 0 ||
+ int(l->count()) >= n.count()-1 ) ) {
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = r->recordType();
+ query->l = s;
+ query->dns->replace( (void*)r, (void*)r );
+ QObject::connect( query, SIGNAL(timeout()),
+ Q3DnsManager::manager(), SLOT(retransmit()) );
+ Q3DnsManager::manager()->transmitQuery( query );
+ } else if ( q < m->queries.size() ) {
+ // if we've found an earlier query for the same
+ // domain/type, subscribe to its answer
+ m->queries[q]->dns->replace( (void*)r, (void*)r );
+ }
+ }
+ }
+ l->first();
+ return l;
+}
+
+
+void Q3DnsDomain::sweep( Q_UINT32 thisSweep )
+{
+ if ( !rrs )
+ return;
+
+ Q3DnsRR * rr;
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ if ( !rr->deleteTime )
+ rr->deleteTime = thisSweep; // will hit next time around
+
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::sweep: %s type %d expires %u %u - %s / %s",
+ rr->domain->name().latin1(), rr->t,
+ rr->expireTime, rr->deleteTime,
+ rr->target.latin1(), rr->address.toString().latin1());
+#endif
+ if ( rr->current == false ||
+ rr->t == Q3Dns::None ||
+ rr->deleteTime <= thisSweep ||
+ rr->expireTime <= thisSweep )
+ rrs->remove();
+ else
+ rrs->next();
+ }
+
+ if ( rrs->isEmpty() ) {
+ delete rrs;
+ rrs = 0;
+ }
+}
+
+
+
+
+// the itsy-bitsy little socket class I don't really need except for
+// so I can subclass and reimplement the slots.
+
+
+Q3DnsSocket::Q3DnsSocket( QObject * parent, const char * name )
+ : QObject( parent, name )
+{
+ // nothing
+}
+
+
+Q3DnsSocket::~Q3DnsSocket()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::cleanCache()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::retransmit()
+{
+ // nothing
+}
+
+
+void Q3DnsSocket::answer()
+{
+ // nothing
+}
+
+
+/*!
+ \class Q3Dns
+ \brief The Q3Dns class provides asynchronous DNS lookups.
+
+ \compat
+
+ Both Windows and Unix provide synchronous DNS lookups; Windows
+ provides some asynchronous support too. At the time of writing
+ neither operating system provides asynchronous support for
+ anything other than hostname-to-address mapping.
+
+ Q3Dns rectifies this shortcoming, by providing asynchronous caching
+ lookups for the record types that we expect modern GUI
+ applications to need in the near future.
+
+ The class is \e not straightforward to use (although it is much
+ simpler than the native APIs); Q3Socket provides much easier to use
+ TCP connection facilities. The aim of Q3Dns is to provide a correct
+ and small API to the DNS and nothing more. (We use "correctness"
+ to mean that the DNS information is correctly cached, and
+ correctly timed out.)
+
+ The API comprises a constructor, functions to set the DNS node
+ (the domain in DNS terminology) and record type (setLabel() and
+ setRecordType()), the corresponding get functions, an isWorking()
+ function to determine whether Q3Dns is working or reading, a
+ resultsReady() signal and query functions for the result.
+
+ There is one query function for each RecordType, namely
+ addresses(), mailServers(), servers(), hostNames() and texts().
+ There are also two generic query functions: canonicalName()
+ returns the name you'll presumably end up using (the exact meaning
+ of this depends on the record type) and qualifiedNames() returns a
+ list of the fully qualified names label() maps to.
+
+ \sa Q3Socket
+*/
+
+/*!
+ Constructs a DNS query object with invalid settings for both the
+ label and the search type.
+*/
+
+Q3Dns::Q3Dns()
+{
+ d = new Q3DnsPrivate;
+ t = None;
+}
+
+
+
+
+/*!
+ Constructs a DNS query object that will return record type \a rr
+ information about \a label.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \a rr defaults to \c A, IPv4 addresses.
+*/
+
+Q3Dns::Q3Dns( const QString & label, RecordType rr )
+{
+ d = new Q3DnsPrivate;
+ t = rr;
+ setLabel( label );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+/*!
+ Constructs a DNS query object that will return record type \a rr
+ information about host address \a address. The label is set to the
+ IN-ADDR.ARPA domain name. This is useful in combination with the
+ \c Ptr record type (e.g. if you want to look up a hostname for a
+ given address).
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \a rr defaults to \c Ptr, that maps addresses to hostnames.
+*/
+
+Q3Dns::Q3Dns( const QHostAddress & address, RecordType rr )
+{
+ d = new Q3DnsPrivate;
+ t = rr;
+ setLabel( address );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+
+/*!
+ Destroys the DNS query object and frees its allocated resources.
+*/
+
+Q3Dns::~Q3Dns()
+{
+ if ( globalManager ) {
+ uint q = 0;
+ Q3DnsManager * m = globalManager;
+ while( q < m->queries.size() ) {
+ Q3DnsQuery * query=m->queries[q];
+ if ( query && query->dns )
+ (void)query->dns->take( (void*) this );
+ q++;
+ }
+
+ }
+
+ delete d;
+ d = 0;
+}
+
+
+
+
+/*!
+ Sets this DNS query object to query for information about \a
+ label.
+
+ This does not change the recordType(), but its isWorking() status
+ will probably change as a result.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+*/
+
+void Q3Dns::setLabel( const QString & label )
+{
+ l = label;
+ d->noNames = false;
+
+ // construct a list of qualified names
+ n.clear();
+ if ( l.length() > 1 && l[(int)l.length()-1] == QLatin1Char('.') ) {
+ n.append( l.left( l.length()-1 ).lower() );
+ } else {
+ int i = l.length();
+ int dots = 0;
+ const int maxDots = 2;
+ while( i && dots < maxDots ) {
+ if ( l[--i] == QLatin1Char('.') )
+ dots++;
+ }
+ if ( dots < maxDots ) {
+ (void)Q3DnsManager::manager(); // create a Q3DnsManager, if it is not already there
+ Q3StrListIterator it( *theDomains );
+ const char * dom;
+ while( (dom=it.current()) != 0 ) {
+ ++it;
+ n.append( l.lower() + QLatin1String(".") + QLatin1String(dom) );
+ }
+ }
+ n.append( l.lower() );
+ }
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop ) {
+ doSynchronousLookup();
+ } else {
+ setStartQueryTimer(); // start query the next time we enter event loop
+ }
+#else
+ setStartQueryTimer(); // start query the next time we enter event loop
+#endif
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
+ int i = 0;
+ for( i = 0; i < (int)n.count(); i++ )
+ qDebug( "Q3Dns::setLabel: %d: %s", i, n[i].ascii() );
+#endif
+}
+
+
+/*!
+ \overload
+
+ Sets this DNS query object to query for information about the host
+ address \a address. The label is set to the IN-ADDR.ARPA domain
+ name. This is useful in combination with the \c Ptr record type
+ (e.g. if you want to look up a hostname for a given address).
+*/
+
+void Q3Dns::setLabel( const QHostAddress & address )
+{
+ setLabel( toInAddrArpaDomain( address ) );
+}
+
+
+/*!
+ \fn QStringList Q3Dns::qualifiedNames() const
+
+ Returns a list of the fully qualified names label() maps to.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 0
+
+*/
+
+
+/*!
+ \fn QString Q3Dns::label() const
+
+ Returns the domain name for which this object returns information.
+
+ \sa setLabel()
+*/
+
+/*!
+ \enum Q3Dns::RecordType
+
+ This enum type defines the record types Q3Dns can handle. The DNS
+ provides many more; these are the ones we've judged to be in
+ current use, useful for GUI programs and important enough to
+ support right away:
+
+ \value None No information. This exists only so that Q3Dns can
+ have a default.
+
+ \value A IPv4 addresses. By far the most common type.
+
+ \value Aaaa IPv6 addresses. So far mostly unused.
+
+ \value Mx Mail eXchanger names. Used for mail delivery.
+
+ \value Srv SeRVer names. Generic record type for finding
+ servers. So far mostly unused.
+
+ \value Cname Canonical names. Maps from nicknames to the true
+ name (the canonical name) for a host.
+
+ \value Ptr name PoinTeRs. Maps from IPv4 or IPv6 addresses to hostnames.
+
+ \value Txt arbitrary TeXT for domains.
+
+ We expect that some support for the
+ \l{http://www.rfc-editor.org/rfc/rfc2535.txt}{RFC 2535}
+ extensions will be added in future versions.
+*/
+
+/*!
+ Sets this object to query for record type \a rr records.
+
+ The DNS lookup is started the next time the application enters the
+ event loop. When the result is found the signal resultsReady() is
+ emitted.
+
+ \sa RecordType
+*/
+
+void Q3Dns::setRecordType( RecordType rr )
+{
+ t = rr;
+ d->noNames = false;
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+/*!
+ \internal
+
+ Private slot for starting the query.
+*/
+void Q3Dns::startQuery()
+{
+ // isWorking() starts the query (if necessary)
+ if ( !isWorking() )
+ emit resultsReady();
+}
+
+/*!
+ The three functions Q3Dns::Q3Dns(QString, RecordType),
+ Q3Dns::setLabel() and Q3Dns::setRecordType() may start a DNS lookup.
+ This function handles setting up the single shot timer.
+*/
+void Q3Dns::setStartQueryTimer()
+{
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( !d->queryTimer && !d->noEventLoop )
+#else
+ if ( !d->queryTimer )
+#endif
+ {
+ // start the query the next time we enter event loop
+ d->queryTimer = new QTimer( this );
+ connect( d->queryTimer, SIGNAL(timeout()),
+ this, SLOT(startQuery()) );
+ d->queryTimer->start( 0, true );
+ }
+}
+
+/*
+ Transforms the host address \a address to the IN-ADDR.ARPA domain
+ name. Returns something indeterminate if you're sloppy or
+ naughty. This function has an IPv4-specific name, but works for
+ IPv6 too.
+*/
+QString Q3Dns::toInAddrArpaDomain( const QHostAddress &address )
+{
+ QString s;
+ if ( address.isNull() ) {
+ // if the address isn't valid, neither of the other two make
+ // cases make sense. better to just return.
+ } else if ( address.isIp4Addr() ) {
+ Q_UINT32 i = address.ip4Addr();
+ s.sprintf( "%d.%d.%d.%d.IN-ADDR.ARPA",
+ i & 0xff, (i >> 8) & 0xff, (i>>16) & 0xff, (i>>24) & 0xff );
+ } else {
+ // RFC 3152. (1886 is deprecated, and clients no longer need to
+ // support it, in practice).
+ Q_IPV6ADDR i = address.toIPv6Address();
+ s = QLatin1String("ip6.arpa");
+ uint b = 0;
+ while( b < 16 ) {
+ s = QString::number( i.c[b]%16, 16 ) + QLatin1String(".") +
+ QString::number( i.c[b]/16, 16 ) + QLatin1String(".") + s;
+ b++;
+ }
+ }
+ return s;
+}
+
+
+/*!
+ \fn Q3Dns::RecordType Q3Dns::recordType() const
+
+ Returns the record type of this DNS query object.
+
+ \sa setRecordType() RecordType
+*/
+
+/*!
+ \fn void Q3Dns::resultsReady()
+
+ This signal is emitted when results are available for one of the
+ qualifiedNames().
+*/
+
+/*!
+ Returns true if Q3Dns is doing a lookup for this object (i.e. if it
+ does not already have the necessary information); otherwise
+ returns false.
+
+ Q3Dns emits the resultsReady() signal when the status changes to false.
+*/
+
+bool Q3Dns::isWorking() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::isWorking (%s, %d)", l.ascii(), t );
+#endif
+ if ( t == None )
+ return false;
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop )
+ return true;
+#endif
+
+ Q3PtrList<Q3DnsRR> * ll = Q3DnsDomain::cached( this );
+ Q_LONG queries = n.count();
+ while( ll->current() != 0 ) {
+ if ( ll->current()->nxdomain ) {
+ queries--;
+ } else {
+ delete ll;
+ return false;
+ }
+ ll->next();
+ }
+ delete ll;
+
+ if ( queries <= 0 )
+ return false;
+ if ( d->noNames )
+ return false;
+ return true;
+}
+
+
+/*!
+ Returns a list of the addresses for this name if this Q3Dns object
+ has a recordType() of Q3Dns::A or Q3Dns::Aaaa and the answer
+ is available; otherwise returns an empty list.
+
+ As a special case, if label() is a valid numeric IP address, this
+ function returns that address.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 1
+
+*/
+
+Q3ValueList<QHostAddress> Q3Dns::addresses() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::addresses (%s)", l.ascii() );
+#endif
+ Q3ValueList<QHostAddress> result;
+ if ( t != A && t != Aaaa )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain )
+ result.append( rr->address );
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ \class Q3Dns::MailServer
+ \brief The Q3Dns::MailServer class is described in Q3Dns::mailServers().
+
+ \internal
+*/
+
+/*!
+ Returns a list of mail servers if the record type is \c Mx. The
+ class Q3Dns::MailServer contains the following public variables:
+ \list
+ \i QString Q3Dns::MailServer::name
+ \i Q_UINT16 Q3Dns::MailServer::priority
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 2
+
+*/
+Q3ValueList<Q3Dns::MailServer> Q3Dns::mailServers() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::mailServers (%s)", l.ascii() );
+#endif
+ Q3ValueList<Q3Dns::MailServer> result;
+ if ( t != Mx )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ MailServer ms( rr->target, rr->priority );
+ result.append( ms );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ \class Q3Dns::Server
+ \brief The Q3Dns::Server class is described in Q3Dns::servers().
+
+ \internal
+*/
+
+/*!
+ Returns a list of servers if the record type is \c Srv. The class
+ Q3Dns::Server contains the following public variables:
+ \list
+ \i QString Q3Dns::Server::name
+ \i Q_UINT16 Q3Dns::Server::priority
+ \i Q_UINT16 Q3Dns::Server::weight
+ \i Q_UINT16 Q3Dns::Server::port
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 3
+*/
+Q3ValueList<Q3Dns::Server> Q3Dns::servers() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::servers (%s)", l.ascii() );
+#endif
+ Q3ValueList<Q3Dns::Server> result;
+ if ( t != Srv )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ Server s( rr->target, rr->priority, rr->weight, rr->port );
+ result.append( s );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns a list of host names if the record type is \c Ptr.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 4
+
+*/
+QStringList Q3Dns::hostNames() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::hostNames (%s)", l.ascii() );
+#endif
+ QStringList result;
+ if ( t != Ptr )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ QString str( rr->target );
+ result.append( str );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns a list of texts if the record type is \c Txt.
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \snippet doc/src/snippets/code/src_qt3support_network_q3dns.cpp 5
+*/
+QStringList Q3Dns::texts() const
+{
+#if defined(Q3DNS_DEBUG)
+ qDebug( "Q3Dns::texts (%s)", l.ascii() );
+#endif
+ QStringList result;
+ if ( t != Txt )
+ return result;
+
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( this );
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ QString str( rr->text );
+ result.append( str );
+ }
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ Returns the canonical name for this DNS node. (This works
+ regardless of what recordType() is set to.)
+
+ If the canonical name isn't known, this function returns a null
+ string.
+
+ The canonical name of a DNS node is its full name, or the full
+ name of the target of its CNAME. For example, if l.trolltech.com
+ is a CNAME to lillian.troll.no, and the search path for Q3Dns is
+ "trolltech.com", then the canonical name for all of "lillian",
+ "l", "lillian.troll.no." and "l.trolltech.com" is
+ "lillian.troll.no.".
+*/
+
+QString Q3Dns::canonicalName() const
+{
+ // the cname should work regardless of the recordType(), so set the record
+ // type temporarily to cname when you look at the cache
+ Q3Dns *that = (Q3Dns*) this; // mutable function
+ RecordType oldType = t;
+ that->t = Cname;
+ Q3PtrList<Q3DnsRR> * cached = Q3DnsDomain::cached( that );
+ that->t = oldType;
+
+ Q3DnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain && rr->domain ) {
+ delete cached;
+ return rr->target;
+ }
+ cached->next();
+ }
+ delete cached;
+ return QString();
+}
+
+#if defined(Q_DNS_SYNCHRONOUS)
+/*! \reimp
+*/
+void Q3Dns::connectNotify( const char *signal )
+{
+ if ( d->noEventLoop && qstrcmp(signal,SIGNAL(resultsReady()) )==0 ) {
+ doSynchronousLookup();
+ }
+}
+#endif
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_CYGWIN)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void Q3Dns::doSynchronousLookup()
+{
+ // ### not implemented yet
+}
+#endif
+
+// the following typedefs are needed for GetNetworkParams() API call
+#ifndef IP_TYPES_INCLUDED
+#define MAX_HOSTNAME_LEN 128
+#define MAX_DOMAIN_NAME_LEN 128
+#define MAX_SCOPE_ID_LEN 256
+typedef struct {
+ char String[4 * 4];
+} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+typedef struct _IP_ADDR_STRING {
+ struct _IP_ADDR_STRING* Next;
+ IP_ADDRESS_STRING IpAddress;
+ IP_MASK_STRING IpMask;
+ DWORD Context;
+} IP_ADDR_STRING, *PIP_ADDR_STRING;
+typedef struct {
+ char HostName[MAX_HOSTNAME_LEN + 4] ;
+ char DomainName[MAX_DOMAIN_NAME_LEN + 4];
+ PIP_ADDR_STRING CurrentDnsServer;
+ IP_ADDR_STRING DnsServerList;
+ UINT NodeType;
+ char ScopeId[MAX_SCOPE_ID_LEN + 4];
+ UINT EnableRouting;
+ UINT EnableProxy;
+ UINT EnableDns;
+} FIXED_INFO, *PFIXED_INFO;
+#endif
+typedef DWORD (WINAPI *GNP)( PFIXED_INFO, PULONG );
+
+// ### FIXME: this code is duplicated in qfiledialog.cpp
+static QString getWindowsRegString( HKEY key, const QString &subKey )
+{
+ QString s;
+ QT_WA( {
+ char buf[1024];
+ DWORD bsz = sizeof(buf);
+ int r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)buf, &bsz );
+ if ( r == ERROR_SUCCESS ) {
+ s = QString::fromUcs2( (unsigned short *)buf );
+ } else if ( r == ERROR_MORE_DATA ) {
+ char *ptr = new char[bsz+1];
+ r = RegQueryValueEx( key, (TCHAR*)subKey.ucs2(), 0, 0, (LPBYTE)ptr, &bsz );
+ if ( r == ERROR_SUCCESS )
+ s = QLatin1String(ptr);
+ delete [] ptr;
+ }
+ } , {
+ char buf[512];
+ DWORD bsz = sizeof(buf);
+ int r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)buf, &bsz );
+ if ( r == ERROR_SUCCESS ) {
+ s = QLatin1String(buf);
+ } else if ( r == ERROR_MORE_DATA ) {
+ char *ptr = new char[bsz+1];
+ r = RegQueryValueExA( key, subKey.local8Bit(), 0, 0, (LPBYTE)ptr, &bsz );
+ if ( r == ERROR_SUCCESS )
+ s = QLatin1String(ptr);
+ delete [] ptr;
+ }
+ } );
+ return s;
+}
+
+static bool getDnsParamsFromRegistry( const QString &path,
+ QString *domainName, QString *nameServer, QString *searchList )
+{
+ HKEY k;
+ int r;
+ QT_WA( {
+ r = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ (TCHAR*)path.ucs2(),
+ 0, KEY_READ, &k );
+ } , {
+ r = RegOpenKeyExA( HKEY_LOCAL_MACHINE,
+ path.latin1(),
+ 0, KEY_READ, &k );
+ } );
+
+ if ( r == ERROR_SUCCESS ) {
+ *domainName = getWindowsRegString( k, QLatin1String("DhcpDomain") );
+ if ( domainName->isEmpty() )
+ *domainName = getWindowsRegString( k, QLatin1String("Domain") );
+
+ *nameServer = getWindowsRegString( k, QLatin1String("DhcpNameServer") );
+ if ( nameServer->isEmpty() )
+ *nameServer = getWindowsRegString( k, QLatin1String("NameServer") );
+
+ *searchList = getWindowsRegString( k, QLatin1String("SearchList") );
+ }
+ RegCloseKey( k );
+ return r == ERROR_SUCCESS;
+}
+
+void Q3Dns::doResInit()
+{
+ char separator = 0;
+
+ if ( theNs )
+ delete theNs;
+ theNs = new Q3PtrList<QHostAddress>;
+ theNs->setAutoDelete( true );
+ theDomains = new Q3StrList( true );
+ theDomains->setAutoDelete( true );
+
+ QString domainName, nameServer, searchList;
+
+ bool gotNetworkParams = false;
+ // try the API call GetNetworkParams() first and use registry lookup only
+ // as a fallback
+#ifdef Q_OS_WINCE
+ HINSTANCE hinstLib = LoadLibraryW( L"iphlpapi" );
+#else
+ HINSTANCE hinstLib = LoadLibraryA( "iphlpapi" );
+#endif
+ if ( hinstLib != 0 ) {
+#ifdef Q_OS_WINCE
+ GNP getNetworkParams = (GNP) GetProcAddressW( hinstLib, L"GetNetworkParams" );
+#else
+ GNP getNetworkParams = (GNP) GetProcAddress( hinstLib, "GetNetworkParams" );
+#endif
+ if ( getNetworkParams != 0 ) {
+ ULONG l = 0;
+ DWORD res;
+ res = getNetworkParams( 0, &l );
+ if ( res == ERROR_BUFFER_OVERFLOW ) {
+ FIXED_INFO *finfo = (FIXED_INFO*)new char[l];
+ res = getNetworkParams( finfo, &l );
+ if ( res == ERROR_SUCCESS ) {
+ domainName = QLatin1String(finfo->DomainName);
+ nameServer = QLatin1String("");
+ IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
+ while ( dnsServer != 0 ) {
+ nameServer += QLatin1String(dnsServer->IpAddress.String);
+ dnsServer = dnsServer->Next;
+ if ( dnsServer != 0 )
+ nameServer += QLatin1String(" ");
+ }
+ searchList = QLatin1String("");
+ separator = ' ';
+ gotNetworkParams = true;
+ }
+ delete[] finfo;
+ }
+ }
+ FreeLibrary( hinstLib );
+ }
+ if ( !gotNetworkParams ) {
+ if ( getDnsParamsFromRegistry(
+ QString( QLatin1String("System\\CurrentControlSet\\Services\\Tcpip\\Parameters") ),
+ &domainName, &nameServer, &searchList )) {
+ // for NT
+ separator = ' ';
+ } else if ( getDnsParamsFromRegistry(
+ QString( QLatin1String("System\\CurrentControlSet\\Services\\VxD\\MSTCP") ),
+ &domainName, &nameServer, &searchList )) {
+ // for Windows 98
+ separator = ',';
+ } else {
+ // Could not access the TCP/IP parameters
+ domainName = QLatin1String("");
+ nameServer = QLatin1String("127.0.0.1");
+ searchList = QLatin1String("");
+ separator = ' ';
+ }
+ }
+
+ nameServer = nameServer.simplifyWhiteSpace();
+ int first, last;
+ if ( !nameServer.isEmpty() ) {
+ first = 0;
+ do {
+ last = nameServer.find( QLatin1Char(separator), first );
+ if ( last < 0 )
+ last = nameServer.length();
+ Q3Dns tmp( nameServer.mid( first, last-first ), Q3Dns::A );
+ Q3ValueList<QHostAddress> address = tmp.addresses();
+ Q_LONG i = address.count();
+ while( i )
+ theNs->append( new QHostAddress(address[--i]) );
+ first = last+1;
+ } while( first < (int)nameServer.length() );
+ }
+
+ searchList = searchList + QLatin1String(" ") + domainName;
+ searchList = searchList.simplifyWhiteSpace().lower();
+ first = 0;
+ do {
+ last = searchList.find( QLatin1Char(separator), first );
+ if ( last < 0 )
+ last = searchList.length();
+ theDomains->append( qstrdup( searchList.mid( first, last-first ).latin1() ) );
+ first = last+1;
+ } while( first < (int)searchList.length() );
+}
+
+#elif defined(Q_OS_UNIX)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void Q3Dns::doSynchronousLookup()
+{
+ if ( t!=None && !l.isEmpty() ) {
+ Q3ValueListIterator<QString> it = n.begin();
+ Q3ValueListIterator<QString> end = n.end();
+ int type;
+ switch( t ) {
+ case Q3Dns::A:
+ type = 1;
+ break;
+ case Q3Dns::Aaaa:
+ type = 28;
+ break;
+ case Q3Dns::Mx:
+ type = 15;
+ break;
+ case Q3Dns::Srv:
+ type = 33;
+ break;
+ case Q3Dns::Cname:
+ type = 5;
+ break;
+ case Q3Dns::Ptr:
+ type = 12;
+ break;
+ case Q3Dns::Txt:
+ type = 16;
+ break;
+ default:
+ type = (char)255; // any
+ break;
+ }
+ while( it != end ) {
+ QString s = *it;
+ it++;
+ QByteArray ba( 512 );
+ int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
+ if ( len > 0 ) {
+ ba.resize( len );
+
+ Q3DnsQuery * query = new Q3DnsQuery;
+ query->started = now();
+ query->id = ++theId;
+ query->t = t;
+ query->l = s;
+ Q3DnsAnswer a( ba, query );
+ a.parse();
+ } else if ( len == -1 ) {
+ // res_search error
+ }
+ }
+ emit resultsReady();
+ }
+}
+#endif
+
+#if defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 3)))
+#define Q_MODERN_RES_API
+#endif
+
+void Q3Dns::doResInit()
+{
+ if ( theNs )
+ return;
+ theNs = new Q3PtrList<QHostAddress>;
+ theNs->setAutoDelete( true );
+ theDomains = new Q3StrList( true );
+ theDomains->setAutoDelete( true );
+
+ // read resolv.conf manually.
+ QFile resolvConf(QLatin1String("/etc/resolv.conf"));
+ if (resolvConf.open(QIODevice::ReadOnly)) {
+ QTextStream stream( &resolvConf );
+ QString line;
+
+ while ( !stream.atEnd() ) {
+ line = stream.readLine();
+ QStringList list = QStringList::split( QLatin1String(" "), line );
+ if( line.startsWith( QLatin1String("#") ) || list.size() < 2 )
+ continue;
+ const QString type = list[0].lower();
+
+ if ( type == QLatin1String("nameserver") ) {
+ QHostAddress *address = new QHostAddress();
+ if ( address->setAddress( QString(list[1]) ) ) {
+ // only add ipv6 addresses from resolv.conf if
+ // this host supports ipv6.
+ if ( address->isIPv4Address() || ipv6support )
+ theNs->append( address );
+ else
+ delete address;
+ } else {
+ delete address;
+ }
+ } else if ( type == QLatin1String("search") ) {
+ QStringList srch = QStringList::split( QLatin1String(" "), list[1] );
+ for ( QStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
+ theDomains->append( (*i).lower().local8Bit() );
+
+ } else if ( type == QLatin1String("domain") ) {
+ theDomains->append( list[1].lower().local8Bit() );
+ }
+ }
+ }
+
+ if (theNs->isEmpty()) {
+#if defined(Q_MODERN_RES_API)
+ struct __res_state res;
+ res_ninit( &res );
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < res.nscount; i++ )
+ theNs->append( new QHostAddress( ntohl( res.nsaddr_list[i].sin_addr.s_addr ) ) );
+# if defined(MAXDFLSRCH)
+ for( i=0; i < MAXDFLSRCH; i++ ) {
+ if ( res.dnsrch[i] && *(res.dnsrch[i]) )
+ theDomains->append( QString::fromLatin1( res.dnsrch[i] ).lower().local8Bit() );
+ else
+ break;
+ }
+# endif
+ if ( *res.defdname )
+ theDomains->append( QString::fromLatin1( res.defdname ).lower().local8Bit() );
+#else
+ res_init();
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < _res.nscount; i++ )
+ theNs->append( new QHostAddress( ntohl( _res.nsaddr_list[i].sin_addr.s_addr ) ) );
+# if defined(MAXDFLSRCH)
+ for( i=0; i < MAXDFLSRCH; i++ ) {
+ if ( _res.dnsrch[i] && *(_res.dnsrch[i]) )
+ theDomains->append( QString::fromLatin1( _res.dnsrch[i] ).lower().local8Bit() );
+ else
+ break;
+ }
+# endif
+ if ( *_res.defdname )
+ theDomains->append( QString::fromLatin1( _res.defdname ).lower().local8Bit() );
+#endif
+
+ // the code above adds "0.0.0.0" as a name server at the slightest
+ // hint of trouble. so remove those again.
+ theNs->first();
+ while( theNs->current() ) {
+ if ( theNs->current()->isNull() )
+ delete theNs->take();
+ else
+ theNs->next();
+ }
+ }
+
+ QFile hosts( QString::fromLatin1( "/etc/hosts" ) );
+ if ( hosts.open( QIODevice::ReadOnly ) ) {
+ // read the /etc/hosts file, creating long-life A and PTR RRs
+ // for the things we find.
+ QTextStream i( &hosts );
+ QString line;
+ while( !i.atEnd() ) {
+ line = i.readLine().simplifyWhiteSpace().lower();
+ uint n = 0;
+ while( (int) n < line.length() && line[(int)n] != QLatin1Char('#') )
+ n++;
+ line.truncate( n );
+ n = 0;
+ while( (int) n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ QString ip = line.left( n );
+ QHostAddress a;
+ a.setAddress( ip );
+ if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
+ bool first = true;
+ line = line.mid( n+1 );
+ n = 0;
+ while( (int) n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ QString hostname = line.left( n );
+ // ### in case of bad syntax, hostname is invalid. do we care?
+ if ( n ) {
+ Q3DnsRR * rr = new Q3DnsRR( hostname );
+ if ( a.isIPv4Address() )
+ rr->t = Q3Dns::A;
+ else
+ rr->t = Q3Dns::Aaaa;
+ rr->address = a;
+ rr->deleteTime = UINT_MAX;
+ rr->expireTime = UINT_MAX;
+ rr->current = true;
+ if ( first ) {
+ first = false;
+ Q3DnsRR * ptr = new Q3DnsRR( Q3Dns::toInAddrArpaDomain( a ) );
+ ptr->t = Q3Dns::Ptr;
+ ptr->target = hostname;
+ ptr->deleteTime = UINT_MAX;
+ ptr->expireTime = UINT_MAX;
+ ptr->current = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DNS
diff --git a/src/qt3support/network/q3dns.h b/src/qt3support/network/q3dns.h
new file mode 100644
index 0000000000..bfe3e8b752
--- /dev/null
+++ b/src/qt3support/network/q3dns.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DNS_H
+#define Q3DNS_H
+
+#include <QtCore/qobject.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtCore/qsocketnotifier.h>
+#include <QtCore/qstringlist.h>
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_DNS
+
+//#define Q_DNS_SYNCHRONOUS
+
+class Q3DnsPrivate;
+
+class Q_COMPAT_EXPORT Q3Dns: public QObject {
+ Q_OBJECT
+public:
+ enum RecordType {
+ None,
+ A, Aaaa,
+ Mx, Srv,
+ Cname,
+ Ptr,
+ Txt
+ };
+
+ Q3Dns();
+ Q3Dns( const QString & label, RecordType rr = A );
+ Q3Dns( const QHostAddress & address, RecordType rr = Ptr );
+ virtual ~Q3Dns();
+
+ // to set/change the query
+ virtual void setLabel( const QString & label );
+ virtual void setLabel( const QHostAddress & address );
+ QString label() const { return l; }
+
+ virtual void setRecordType( RecordType rr = A );
+ RecordType recordType() const { return t; }
+
+ // whether something is happening behind the scenes
+ bool isWorking() const;
+
+ // to query for replies
+ Q3ValueList<QHostAddress> addresses() const;
+
+ class Q_COMPAT_EXPORT MailServer {
+ public:
+ MailServer( const QString & n=QString(), Q_UINT16 p=0 )
+ :name(n), priority(p) {}
+ QString name;
+ Q_UINT16 priority;
+ Q_DUMMY_COMPARISON_OPERATOR(MailServer)
+ };
+ Q3ValueList<MailServer> mailServers() const;
+
+ class Q_COMPAT_EXPORT Server {
+ public:
+ Server(const QString & n=QString(), Q_UINT16 p=0, Q_UINT16 w=0, Q_UINT16 po=0 )
+ : name(n), priority(p), weight(w), port(po) {}
+ QString name;
+ Q_UINT16 priority;
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ Q_DUMMY_COMPARISON_OPERATOR(Server)
+ };
+ Q3ValueList<Server> servers() const;
+
+ QStringList hostNames() const;
+
+ QStringList texts() const;
+
+ QString canonicalName() const; // ### real-world but uncommon: QStringList
+
+ QStringList qualifiedNames() const { return n; }
+
+#if defined(Q_DNS_SYNCHRONOUS)
+protected:
+ void connectNotify( const char *signal );
+#endif
+
+Q_SIGNALS:
+ void resultsReady();
+
+private Q_SLOTS:
+ void startQuery();
+
+private:
+ static void doResInit();
+ void setStartQueryTimer();
+ static QString toInAddrArpaDomain( const QHostAddress &address );
+#if defined(Q_DNS_SYNCHRONOUS)
+ void doSynchronousLookup();
+#endif
+
+ QString l;
+ QStringList n;
+ RecordType t;
+ Q3DnsPrivate * d;
+
+ friend class Q3DnsAnswer;
+ friend class Q3DnsManager;
+};
+
+
+// Q3DnsSocket are sockets that are used for DNS lookup
+
+class Q3DnsSocket: public QObject {
+ Q_OBJECT
+ // note: Private not public. This class contains NO public API.
+protected:
+ Q3DnsSocket( QObject *, const char * );
+ virtual ~Q3DnsSocket();
+
+private Q_SLOTS:
+ virtual void cleanCache();
+ virtual void retransmit();
+ virtual void answer();
+};
+
+#endif // QT_NO_DNS
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3DNS_H
diff --git a/src/qt3support/network/q3ftp.cpp b/src/qt3support/network/q3ftp.cpp
new file mode 100644
index 0000000000..5ab84cc06f
--- /dev/null
+++ b/src/qt3support/network/q3ftp.cpp
@@ -0,0 +1,2378 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include "q3ftp.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+
+#include "q3socket.h"
+#include "q3socketdevice.h"
+#include "qurlinfo.h"
+#include "q3urloperator.h"
+#include "qstringlist.h"
+#include "qregexp.h"
+#include "qtimer.h"
+#include "qfileinfo.h"
+#include "q3ptrdict.h"
+#include "q3cstring.h"
+#include "qcoreapplication.h"
+#include "qftp.h"
+
+#ifndef QT_NO_TEXTCODEC
+#include "qtextcodec.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3FTPPI_DEBUG
+//#define Q3FTPDTP_DEBUG
+
+class Q3FtpPI;
+
+class Q3FtpDTP : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ConnectState {
+ CsHostFound,
+ CsConnected,
+ CsClosed,
+ CsHostNotFound,
+ CsConnectionRefused
+ };
+
+ Q3FtpDTP( Q3FtpPI *p, QObject *parent=0, const char *name=0 );
+
+ void setData( QByteArray * );
+ void setDevice( QIODevice * );
+ void writeData();
+
+ void setBytesTotal( int bytes )
+ {
+ bytesTotal = bytes;
+ bytesDone = 0;
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ }
+
+ bool hasError() const;
+ QString errorMessage() const;
+ void clearError();
+
+ void connectToHost( const QString & host, Q_UINT16 port )
+ { socket.connectToHost( host, port ); }
+
+ Q3Socket::State socketState() const
+ { return socket.state(); }
+
+ Q_ULONG bytesAvailable() const
+ { return socket.bytesAvailable(); }
+
+ Q_LONG readBlock( char *data, Q_ULONG maxlen )
+ {
+ Q_LONG read = socket.readBlock( data, maxlen );
+ bytesDone += read;
+ return read;
+ }
+
+ QByteArray readAll()
+ {
+ QByteArray tmp = socket.readAll();
+ bytesDone += tmp.size();
+ return tmp;
+ }
+
+ void abortConnection();
+
+ static bool parseDir( const QString &buffer, const QString &userName, QUrlInfo *info );
+
+signals:
+ void listInfo( const QUrlInfo& );
+ void readyRead();
+ void dataTransferProgress( int, int );
+
+ void connectState( int );
+
+private slots:
+ void socketConnected();
+ void socketReadyRead();
+ void socketError( int );
+ void socketConnectionClosed();
+ void socketBytesWritten( int );
+
+private:
+ void clearData()
+ {
+ is_ba = false;
+ data.dev = 0;
+ }
+
+ Q3Socket socket;
+ Q3FtpPI *pi;
+ QString err;
+ int bytesDone;
+ int bytesTotal;
+ bool callWriteData;
+
+ // If is_ba is true, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+};
+
+class Q3FtpPI : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q3FtpPI( QObject *parent = 0 );
+
+ void connectToHost( const QString &host, Q_UINT16 port );
+
+ bool sendCommands( const QStringList &cmds );
+ bool sendCommand( const QString &cmd )
+ { return sendCommands( QStringList( cmd ) ); }
+
+ void clearPendingCommands();
+ void abort();
+
+ QString currentCommand() const
+ { return currentCmd; }
+
+ bool rawCommand;
+
+ Q3FtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it
+ // makes the design simpler this way
+signals:
+ void connectState( int );
+ void finished( const QString& );
+ void error( int, const QString& );
+ void rawFtpReply( int, const QString& );
+
+private slots:
+ void hostFound();
+ void connected();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void error( int );
+
+ void dtpConnectState( int );
+
+private:
+ // the states are modelled after the generalized state diagram of RFC 959,
+ // page 58
+ enum State {
+ Begin,
+ Idle,
+ Waiting,
+ Success,
+ Failure
+ };
+
+ enum AbortState {
+ None,
+ AbortStarted,
+ WaitForAbortToFinish
+ };
+
+ bool processReply();
+ bool startNextCmd();
+
+ Q3Socket commandSocket;
+ QString replyText;
+ signed char replyCode[3];
+ State state;
+ AbortState abortState;
+ QStringList pendingCommands;
+ QString currentCmd;
+
+ bool waitForDtpToConnect;
+ bool waitForDtpToClose;
+};
+
+/**********************************************************************
+ *
+ * Q3FtpCommand implemenatation
+ *
+ *********************************************************************/
+class Q3FtpCommand
+{
+public:
+ Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw );
+ Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, const QByteArray &ba );
+ Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, QIODevice *dev );
+ ~Q3FtpCommand();
+
+ int id;
+ Q3Ftp::Command command;
+ QStringList rawCmds;
+
+ // If is_ba is true, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+
+ static int idCounter;
+};
+
+int Q3FtpCommand::idCounter = 0;
+
+Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw )
+ : command(cmd), rawCmds(raw), is_ba(false)
+{
+ id = ++idCounter;
+ data.dev = 0;
+}
+
+Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, const QByteArray &ba )
+ : command(cmd), rawCmds(raw), is_ba(true)
+{
+ id = ++idCounter;
+ data.ba = new QByteArray( ba );
+}
+
+Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, QIODevice *dev )
+ : command(cmd), rawCmds(raw), is_ba(false)
+{
+ id = ++idCounter;
+ data.dev = dev;
+}
+
+Q3FtpCommand::~Q3FtpCommand()
+{
+ if ( is_ba )
+ delete data.ba;
+}
+
+/**********************************************************************
+ *
+ * Q3FtpDTP implemenatation
+ *
+ *********************************************************************/
+Q3FtpDTP::Q3FtpDTP( Q3FtpPI *p, QObject *parent, const char *name ) :
+ QObject( parent, name ),
+ socket( 0, "Q3FtpDTP_socket" ),
+ pi( p ),
+ callWriteData( false )
+{
+ clearData();
+
+ connect( &socket, SIGNAL(connected()),
+ SLOT(socketConnected()) );
+ connect( &socket, SIGNAL(readyRead()),
+ SLOT(socketReadyRead()) );
+ connect( &socket, SIGNAL(error(int)),
+ SLOT(socketError(int)) );
+ connect( &socket, SIGNAL(connectionClosed()),
+ SLOT(socketConnectionClosed()) );
+ connect( &socket, SIGNAL(bytesWritten(int)),
+ SLOT(socketBytesWritten(int)) );
+}
+
+void Q3FtpDTP::setData( QByteArray *ba )
+{
+ is_ba = true;
+ data.ba = ba;
+}
+
+void Q3FtpDTP::setDevice( QIODevice *dev )
+{
+ is_ba = false;
+ data.dev = dev;
+}
+
+void Q3FtpDTP::writeData()
+{
+ if ( is_ba ) {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::writeData: write %d bytes", data.ba->size() );
+#endif
+ if ( data.ba->size() == 0 )
+ emit dataTransferProgress( 0, bytesTotal );
+ else
+ socket.writeBlock( data.ba->data(), data.ba->size() );
+ socket.close();
+ clearData();
+ } else if ( data.dev ) {
+ callWriteData = false;
+ const int blockSize = 16*1024;
+ char buf[blockSize];
+ while ( !data.dev->atEnd() && socket.bytesToWrite()==0 ) {
+ Q_LONG read = data.dev->readBlock( buf, blockSize );
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::writeData: writeBlock() of size %d bytes", (int)read );
+#endif
+ socket.writeBlock( buf, read );
+ if ( !data.dev )
+ return; // this can happen when a command is aborted
+ }
+ if ( data.dev->atEnd() ) {
+ if ( bytesDone==0 && socket.bytesToWrite()==0 )
+ emit dataTransferProgress( 0, bytesTotal );
+ socket.close();
+ clearData();
+ } else {
+ callWriteData = true;
+ }
+ }
+}
+
+inline bool Q3FtpDTP::hasError() const
+{
+ return !err.isNull();
+}
+
+inline QString Q3FtpDTP::errorMessage() const
+{
+ return err;
+}
+
+inline void Q3FtpDTP::clearError()
+{
+ err.clear();
+}
+
+void Q3FtpDTP::abortConnection()
+{
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::abortConnection" );
+#endif
+ callWriteData = false;
+ clearData();
+
+ socket.clearPendingData();
+ socket.close();
+}
+
+bool Q3FtpDTP::parseDir( const QString &buffer, const QString &userName, QUrlInfo *info )
+{
+ QStringList lst = QStringList::split( QLatin1String(" "), buffer );
+
+ if ( lst.count() < 9 )
+ return false;
+
+ QString tmp;
+
+ // permissions
+ tmp = lst[ 0 ];
+
+ if ( tmp[ 0 ] == QChar( QLatin1Char('d') ) ) {
+ info->setDir( true );
+ info->setFile( false );
+ info->setSymLink( false );
+ } else if ( tmp[ 0 ] == QChar( QLatin1Char('-') ) ) {
+ info->setDir( false );
+ info->setFile( true );
+ info->setSymLink( false );
+ } else if ( tmp[ 0 ] == QChar( QLatin1Char('l') ) ) {
+ info->setDir( true ); // #### todo
+ info->setFile( false );
+ info->setSymLink( true );
+ } else {
+ return false;
+ }
+
+ static int user = 0;
+ static int group = 1;
+ static int other = 2;
+ static int readable = 0;
+ static int writable = 1;
+ static int executable = 2;
+
+ bool perms[ 3 ][ 3 ];
+ perms[0][0] = (tmp[ 1 ] == QLatin1Char('r'));
+ perms[0][1] = (tmp[ 2 ] == QLatin1Char('w'));
+ perms[0][2] = (tmp[ 3 ] == QLatin1Char('x'));
+ perms[1][0] = (tmp[ 4 ] == QLatin1Char('r'));
+ perms[1][1] = (tmp[ 5 ] == QLatin1Char('w'));
+ perms[1][2] = (tmp[ 6 ] == QLatin1Char('x'));
+ perms[2][0] = (tmp[ 7 ] == QLatin1Char('r'));
+ perms[2][1] = (tmp[ 8 ] == QLatin1Char('w'));
+ perms[2][2] = (tmp[ 9 ] == QLatin1Char('x'));
+
+ // owner
+ tmp = lst[ 2 ];
+ info->setOwner( tmp );
+
+ // group
+ tmp = lst[ 3 ];
+ info->setGroup( tmp );
+
+ // ### not correct
+ info->setWritable( ( userName == info->owner() && perms[ user ][ writable ] ) ||
+ perms[ other ][ writable ] );
+ info->setReadable( ( userName == info->owner() && perms[ user ][ readable ] ) ||
+ perms[ other ][ readable ] );
+
+ int p = 0;
+ if ( perms[ user ][ readable ] )
+ p |= QUrlInfo::ReadOwner;
+ if ( perms[ user ][ writable ] )
+ p |= QUrlInfo::WriteOwner;
+ if ( perms[ user ][ executable ] )
+ p |= QUrlInfo::ExeOwner;
+ if ( perms[ group ][ readable ] )
+ p |= QUrlInfo::ReadGroup;
+ if ( perms[ group ][ writable ] )
+ p |= QUrlInfo::WriteGroup;
+ if ( perms[ group ][ executable ] )
+ p |= QUrlInfo::ExeGroup;
+ if ( perms[ other ][ readable ] )
+ p |= QUrlInfo::ReadOther;
+ if ( perms[ other ][ writable ] )
+ p |= QUrlInfo::WriteOther;
+ if ( perms[ other ][ executable ] )
+ p |= QUrlInfo::ExeOther;
+ info->setPermissions( p );
+
+ // size
+ tmp = lst[ 4 ];
+ info->setSize( tmp.toInt() );
+
+ // date and time
+ QTime time;
+ QString dateStr;
+ dateStr += QLatin1String("Sun ");
+ lst[ 5 ][ 0 ] = lst[ 5 ][ 0 ].upper();
+ dateStr += lst[ 5 ];
+ dateStr += QLatin1Char(' ');
+ dateStr += lst[ 6 ];
+ dateStr += QLatin1Char(' ');
+
+ if ( lst[ 7 ].contains( QLatin1String(":") ) ) {
+ time = QTime( lst[ 7 ].left( 2 ).toInt(), lst[ 7 ].right( 2 ).toInt() );
+ dateStr += QString::number( QDate::currentDate().year() );
+ } else {
+ dateStr += lst[ 7 ];
+ }
+
+ QDate date = QDate::fromString( dateStr );
+ info->setLastModified( QDateTime( date, time ) );
+
+ if ( lst[ 7 ].contains( QLatin1String(":") ) ) {
+ const int futureTolerance = 600;
+ if( info->lastModified().secsTo( QDateTime::currentDateTime() ) < -futureTolerance ) {
+ QDateTime dt = info->lastModified();
+ QDate d = dt.date();
+ d.setYMD(d.year()-1, d.month(), d.day());
+ dt.setDate(d);
+ info->setLastModified(dt);
+ }
+ }
+
+ // name
+ if ( info->isSymLink() )
+ info->setName( lst[ 8 ].stripWhiteSpace() );
+ else {
+ QString n;
+ for ( uint i = 8; i < (uint) lst.count(); ++i )
+ n += lst[ i ] + QLatin1String(" ");
+ n = n.stripWhiteSpace();
+ info->setName( n );
+ }
+ return true;
+}
+
+void Q3FtpDTP::socketConnected()
+{
+#if !defined (Q_WS_QWS)
+ // Use a large send buffer to reduce the number
+ // of writeBlocks when download and uploading files.
+ // The actual size used here (128k) is default on most
+ // Unixes.
+ socket.socketDevice()->setSendBufferSize(128 * 1024);
+ socket.socketDevice()->setReceiveBufferSize(128 * 1024);
+#endif
+
+ bytesDone = 0;
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsConnected )" );
+#endif
+ emit connectState( Q3FtpDTP::CsConnected );
+}
+
+void Q3FtpDTP::socketReadyRead()
+{
+ if ( pi->currentCommand().isEmpty() ) {
+ socket.close();
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsClosed )" );
+#endif
+ emit connectState( Q3FtpDTP::CsClosed );
+ return;
+ }
+
+ if ( pi->currentCommand().startsWith(QLatin1String("LIST")) ) {
+ while ( socket.canReadLine() ) {
+ QUrlInfo i;
+ QString line = QLatin1String(socket.readLine());
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP read (list): '%s'", line.latin1() );
+#endif
+ if ( parseDir( line, QLatin1String(""), &i ) ) {
+ emit listInfo( i );
+ } else {
+ // some FTP servers don't return a 550 if the file or directory
+ // does not exist, but rather write a text to the data socket
+ // -- try to catch these cases
+ if ( line.endsWith( QLatin1String("No such file or directory\r\n") ) )
+ err = line;
+ }
+ }
+ } else {
+ if ( !is_ba && data.dev ) {
+ QByteArray ba( socket.bytesAvailable() );
+ Q_LONG bytesRead = socket.readBlock( ba.data(), ba.size() );
+ if ( bytesRead < 0 ) {
+ // ### error handling
+ return;
+ }
+ ba.resize( bytesRead );
+ bytesDone += bytesRead;
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP read: %d bytes (total %d bytes)", (int)bytesRead, bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ if (data.dev) // make sure it wasn't deleted in the slot
+ data.dev->writeBlock( ba );
+ } else {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP readyRead: %d bytes available (total %d bytes read)", (int)bytesAvailable(), bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone+socket.bytesAvailable(), bytesTotal );
+ emit readyRead();
+ }
+ }
+}
+
+void Q3FtpDTP::socketError( int e )
+{
+ if ( e == Q3Socket::ErrHostNotFound ) {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsHostNotFound )" );
+#endif
+ emit connectState( Q3FtpDTP::CsHostNotFound );
+ } else if ( e == Q3Socket::ErrConnectionRefused ) {
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsConnectionRefused )" );
+#endif
+ emit connectState( Q3FtpDTP::CsConnectionRefused );
+ }
+}
+
+void Q3FtpDTP::socketConnectionClosed()
+{
+ if ( !is_ba && data.dev ) {
+ clearData();
+ }
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::connectState( CsClosed )" );
+#endif
+ emit connectState( Q3FtpDTP::CsClosed );
+}
+
+void Q3FtpDTP::socketBytesWritten( int bytes )
+{
+ bytesDone += bytes;
+#if defined(Q3FTPDTP_DEBUG)
+ qDebug( "Q3FtpDTP::bytesWritten( %d )", bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ if ( callWriteData )
+ writeData();
+}
+
+/**********************************************************************
+ *
+ * Q3FtpPI implemenatation
+ *
+ *********************************************************************/
+Q3FtpPI::Q3FtpPI( QObject *parent ) :
+ QObject( parent ),
+ rawCommand(false),
+ dtp( this ),
+ commandSocket( 0, "Q3FtpPI_socket" ),
+ state( Begin ), abortState( None ),
+ currentCmd( QString() ),
+ waitForDtpToConnect( false ),
+ waitForDtpToClose( false )
+{
+ connect( &commandSocket, SIGNAL(hostFound()),
+ SLOT(hostFound()) );
+ connect( &commandSocket, SIGNAL(connected()),
+ SLOT(connected()) );
+ connect( &commandSocket, SIGNAL(connectionClosed()),
+ SLOT(connectionClosed()) );
+ connect( &commandSocket, SIGNAL(delayedCloseFinished()),
+ SLOT(delayedCloseFinished()) );
+ connect( &commandSocket, SIGNAL(readyRead()),
+ SLOT(readyRead()) );
+ connect( &commandSocket, SIGNAL(error(int)),
+ SLOT(error(int)) );
+
+ connect( &dtp, SIGNAL(connectState(int)),
+ SLOT(dtpConnectState(int)) );
+}
+
+void Q3FtpPI::connectToHost( const QString &host, Q_UINT16 port )
+{
+ emit connectState( Q3Ftp::HostLookup );
+ commandSocket.connectToHost( host, port );
+}
+
+/*
+ Sends the sequence of commands \a cmds to the FTP server. When the commands
+ are all done the finished() signal is emitted. When an error occurs, the
+ error() signal is emitted.
+
+ If there are pending commands in the queue this functions returns false and
+ the \a cmds are not added to the queue; otherwise it returns true.
+*/
+bool Q3FtpPI::sendCommands( const QStringList &cmds )
+{
+ if ( !pendingCommands.isEmpty() )
+ return false;
+
+ if ( commandSocket.state()!=Q3Socket::Connected || state!=Idle ) {
+ emit error( Q3Ftp::NotConnected, QFtp::tr( "Not connected" ) );
+ return true; // there are no pending commands
+ }
+
+ pendingCommands = cmds;
+ startNextCmd();
+ return true;
+}
+
+void Q3FtpPI::clearPendingCommands()
+{
+ pendingCommands.clear();
+ dtp.abortConnection();
+ currentCmd.clear();
+ state = Idle;
+}
+
+void Q3FtpPI::abort()
+{
+ pendingCommands.clear();
+
+ if ( abortState != None )
+ // ABOR already sent
+ return;
+
+ abortState = AbortStarted;
+#if defined(Q3FTPPI_DEBUG)
+ qDebug( "Q3FtpPI send: ABOR" );
+#endif
+ commandSocket.writeBlock( "ABOR\r\n", 6 );
+
+ if ( currentCmd.startsWith(QLatin1String("STOR ")) )
+ dtp.abortConnection();
+}
+
+void Q3FtpPI::hostFound()
+{
+ emit connectState( Q3Ftp::Connecting );
+}
+
+void Q3FtpPI::connected()
+{
+ state = Begin;
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [connected()]", state );
+#endif
+ emit connectState( Q3Ftp::Connected );
+}
+
+void Q3FtpPI::connectionClosed()
+{
+ commandSocket.close();
+ emit connectState( Q3Ftp::Unconnected );
+}
+
+void Q3FtpPI::delayedCloseFinished()
+{
+ emit connectState( Q3Ftp::Unconnected );
+}
+
+void Q3FtpPI::error( int e )
+{
+ if ( e == Q3Socket::ErrHostNotFound ) {
+ emit connectState( Q3Ftp::Unconnected );
+ emit error( Q3Ftp::HostNotFound,
+ QFtp::tr( "Host %1 not found" ).arg( commandSocket.peerName() ) );
+ } else if ( e == Q3Socket::ErrConnectionRefused ) {
+ emit connectState( Q3Ftp::Unconnected );
+ emit error( Q3Ftp::ConnectionRefused,
+ QFtp::tr( "Connection refused to host %1" ).arg( commandSocket.peerName() ) );
+ }
+}
+
+void Q3FtpPI::readyRead()
+{
+ if ( waitForDtpToClose )
+ return;
+
+ while ( commandSocket.canReadLine() ) {
+ // read line with respect to line continuation
+ QString line = QLatin1String(commandSocket.readLine());
+ if ( replyText.isEmpty() ) {
+ if ( line.length() < 3 ) {
+ // ### protocol error
+ return;
+ }
+ const int lowerLimit[3] = {1,0,0};
+ const int upperLimit[3] = {5,5,9};
+ for ( int i=0; i<3; i++ ) {
+ replyCode[i] = line[i].digitValue();
+ if ( replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i] ) {
+ // ### protocol error
+ return;
+ }
+ }
+ }
+ QString endOfMultiLine;
+ endOfMultiLine[0] = '0' + replyCode[0];
+ endOfMultiLine[1] = '0' + replyCode[1];
+ endOfMultiLine[2] = '0' + replyCode[2];
+ endOfMultiLine[3] = ' ';
+ QString lineCont( endOfMultiLine );
+ lineCont[3] = '-';
+ QString lineLeft4 = line.left(4);
+
+ while ( lineLeft4 != endOfMultiLine ) {
+ if ( lineLeft4 == lineCont )
+ replyText += line.mid( 4 ); // strip 'xyz-'
+ else
+ replyText += line;
+ if ( !commandSocket.canReadLine() )
+ return;
+ line = QLatin1String(commandSocket.readLine());
+ lineLeft4 = line.left(4);
+ }
+ replyText += line.mid( 4 ); // strip reply code 'xyz '
+ if ( replyText.endsWith(QLatin1String("\r\n")) )
+ replyText.truncate( replyText.length()-2 );
+
+ if ( processReply() )
+ replyText = QLatin1String("");
+ }
+}
+
+/*
+ Process a reply from the FTP server.
+
+ Returns true if the reply was processed or false if the reply has to be
+ processed at a later point.
+*/
+bool Q3FtpPI::processReply()
+{
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [processReply() begin]", state );
+ if ( replyText.length() < 400 )
+ qDebug( "Q3FtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.latin1() );
+ else
+ qDebug( "Q3FtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2] );
+#endif
+
+ // process 226 replies ("Closing Data Connection") only when the data
+ // connection is really closed to avoid short reads of the DTP
+ if ( 100*replyCode[0]+10*replyCode[1]+replyCode[2] == 226 ) {
+ if ( dtp.socketState() != Q3Socket::Idle ) {
+ waitForDtpToClose = true;
+ return false;
+ }
+ }
+
+ switch ( abortState ) {
+ case AbortStarted:
+ abortState = WaitForAbortToFinish;
+ break;
+ case WaitForAbortToFinish:
+ abortState = None;
+ return true;
+ default:
+ break;
+ }
+
+ // get new state
+ static const State table[5] = {
+ /* 1yz 2yz 3yz 4yz 5yz */
+ Waiting, Success, Idle, Failure, Failure
+ };
+ switch ( state ) {
+ case Begin:
+ if ( replyCode[0] == 1 ) {
+ return true;
+ } else if ( replyCode[0] == 2 ) {
+ state = Idle;
+ emit finished( QFtp::tr( "Connected to host %1" ).arg( commandSocket.peerName() ) );
+ break;
+ }
+ // ### error handling
+ return true;
+ case Waiting:
+ if ( replyCode[0]<0 || replyCode[0]>5 )
+ state = Failure;
+ else
+#if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
+ {
+ // work around a crash on 64 bit gcc IRIX
+ State *t = (State *) table;
+ state = t[replyCode[0] - 1];
+ }
+#else
+ state = table[replyCode[0] - 1];
+#endif
+ break;
+ default:
+ // ### spontaneous message
+ return true;
+ }
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [processReply() intermediate]", state );
+#endif
+
+ // special actions on certain replies
+ int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
+ emit rawFtpReply( replyCodeInt, replyText );
+ if ( rawCommand ) {
+ rawCommand = false;
+ } else if ( replyCodeInt == 227 ) {
+ // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
+ // rfc959 does not define this response precisely, and gives
+ // both examples where the parenthesis are used, and where
+ // they are missing. We need to scan for the address and host
+ // info.
+ QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
+ if (addrPortPattern.search(replyText) == -1) {
+#if defined(Q3FTPPI_DEBUG)
+ qDebug( "Q3Ftp: bad 227 response -- address and port information missing" );
+#endif
+ // ### error handling
+ } else {
+ QStringList lst = addrPortPattern.capturedTexts();
+ QString host = lst[1] + QLatin1String(".") + lst[2] + QLatin1String(".") + lst[3] + QLatin1String(".") + lst[4];
+ Q_UINT16 port = ( lst[5].toUInt() << 8 ) + lst[6].toUInt();
+ waitForDtpToConnect = true;
+ dtp.connectToHost( host, port );
+ }
+ } else if ( replyCodeInt == 230 ) {
+ if ( currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
+ pendingCommands.first().startsWith(QLatin1String("PASS ")) ) {
+ // no need to send the PASS -- we are already logged in
+ pendingCommands.pop_front();
+ }
+ // 230 User logged in, proceed.
+ emit connectState( Q3Ftp::LoggedIn );
+ } else if ( replyCodeInt == 213 ) {
+ // 213 File status.
+ if ( currentCmd.startsWith(QLatin1String("SIZE ")) )
+ dtp.setBytesTotal( replyText.simplifyWhiteSpace().toInt() );
+ } else if ( replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR ")) ) {
+ dtp.writeData();
+ }
+
+ // react on new state
+ switch ( state ) {
+ case Begin:
+ // ### should never happen
+ break;
+ case Success:
+ // ### success handling
+ state = Idle;
+ // no break!
+ case Idle:
+ if ( dtp.hasError() ) {
+ emit error( Q3Ftp::UnknownError, dtp.errorMessage() );
+ dtp.clearError();
+ }
+ startNextCmd();
+ break;
+ case Waiting:
+ // ### do nothing
+ break;
+ case Failure:
+ emit error( Q3Ftp::UnknownError, replyText );
+ state = Idle;
+ startNextCmd();
+ break;
+ }
+#if defined(Q3FTPPI_DEBUG)
+// qDebug( "Q3FtpPI state: %d [processReply() end]", state );
+#endif
+ return true;
+}
+
+#ifndef QT_NO_TEXTCODEC
+Q_COMPAT_EXPORT QTextCodec *qt_ftp_filename_codec = 0;
+#endif
+
+/*
+ Starts next pending command. Returns false if there are no pending commands,
+ otherwise it returns true.
+*/
+bool Q3FtpPI::startNextCmd()
+{
+ if ( waitForDtpToConnect )
+ // don't process any new commands until we are connected
+ return true;
+
+#if defined(Q3FTPPI_DEBUG)
+ if ( state != Idle )
+ qDebug( "Q3FtpPI startNextCmd: Internal error! Q3FtpPI called in non-Idle state %d", state );
+#endif
+ if ( pendingCommands.isEmpty() ) {
+ currentCmd.clear();
+ emit finished( replyText );
+ return false;
+ }
+ currentCmd = pendingCommands.first();
+ pendingCommands.pop_front();
+#if defined(Q3FTPPI_DEBUG)
+ qDebug( "Q3FtpPI send: %s", currentCmd.left( currentCmd.length()-2 ).latin1() );
+#endif
+ state = Waiting;
+#ifndef QT_NO_TEXTCODEC
+ if ( qt_ftp_filename_codec ) {
+ int len;
+ Q3CString enc = qt_ftp_filename_codec->fromUnicode(currentCmd,len);
+ commandSocket.writeBlock( enc.data(), len );
+ } else
+#endif
+ {
+ commandSocket.writeBlock( currentCmd.latin1(), currentCmd.length() );
+ }
+ return true;
+}
+
+void Q3FtpPI::dtpConnectState( int s )
+{
+ switch ( s ) {
+ case Q3FtpDTP::CsClosed:
+ if ( waitForDtpToClose ) {
+ // there is an unprocessed reply
+ if ( processReply() )
+ replyText = QLatin1String("");
+ else
+ return;
+ }
+ waitForDtpToClose = false;
+ readyRead();
+ return;
+ case Q3FtpDTP::CsConnected:
+ waitForDtpToConnect = false;
+ startNextCmd();
+ return;
+ case Q3FtpDTP::CsHostNotFound:
+ case Q3FtpDTP::CsConnectionRefused:
+ emit error( Q3Ftp::ConnectionRefused,
+ QFtp::tr( "Connection refused for data connection" ) );
+ startNextCmd();
+ return;
+ default:
+ return;
+ }
+}
+
+/**********************************************************************
+ *
+ * Q3FtpPrivate
+ *
+ *********************************************************************/
+class Q3FtpPrivate
+{
+public:
+ Q3FtpPrivate() :
+ close_waitForStateChange(false),
+ state( Q3Ftp::Unconnected ),
+ error( Q3Ftp::NoError ),
+ npWaitForLoginDone( false )
+ { pending.setAutoDelete( true ); }
+
+ Q3FtpPI pi;
+ Q3PtrList<Q3FtpCommand> pending;
+ bool close_waitForStateChange;
+ Q3Ftp::State state;
+ Q3Ftp::Error error;
+ QString errorString;
+
+ bool npWaitForLoginDone;
+};
+
+static Q3PtrDict<Q3FtpPrivate> *d_ptr = 0;
+static void cleanup_d_ptr()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+static Q3FtpPrivate* dHelper( const Q3Ftp* foo )
+{
+ if ( !d_ptr ) {
+ d_ptr = new Q3PtrDict<Q3FtpPrivate>;
+ d_ptr->setAutoDelete( true );
+ qAddPostRoutine( cleanup_d_ptr );
+ }
+ Q3FtpPrivate* ret = d_ptr->find( (void*)foo );
+ if ( ! ret ) {
+ ret = new Q3FtpPrivate;
+ d_ptr->replace( (void*) foo, ret );
+ }
+ return ret;
+}
+
+static void delete_d( const Q3Ftp* foo )
+{
+ if ( d_ptr )
+ d_ptr->remove( (void*) foo );
+}
+
+/**********************************************************************
+ *
+ * Q3Ftp implementation
+ *
+ *********************************************************************/
+/*!
+ \class Q3Ftp
+ \brief The Q3Ftp class provides an implementation of the FTP protocol.
+
+ \compat
+
+ This class provides two different interfaces: one is the
+ QNetworkProtocol interface that allows you to use FTP through the
+ QUrlOperator abstraction. The other is a direct interface to FTP
+ that gives you lower-level access to the FTP protocol for finer
+ control. Using the direct interface you can also execute arbitrary
+ FTP commands.
+
+ Don't mix the two interfaces, since the behavior is not
+ well-defined.
+
+ If you want to use Q3Ftp with the QNetworkProtocol interface, you
+ do not use it directly, but rather through a QUrlOperator, for
+ example:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 0
+
+ This code will only work if the Q3Ftp class is registered; to
+ register the class, you must call q3InitNetworkProtocols() before
+ using a QUrlOperator with Q3Ftp.
+
+ The rest of this descrption describes the direct interface to FTP.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "commands"
+ in the rest of the documentation) are the following:
+ connectToHost(), login(), close(), list(), cd(), get(), put(),
+ remove(), mkdir(), rmdir(), rename() and rawCommand().
+
+ All of these commands return a unique identifier that allows you
+ to keep track of the command that is currently being executed.
+ When the execution of a command starts, the commandStarted()
+ signal with the command's identifier is emitted. When the command
+ is finished, the commandFinished() signal is emitted with the
+ command's identifier and a bool that indicates whether the command
+ finished with an error.
+
+ In some cases, you might want to execute a sequence of commands,
+ e.g. if you want to connect and login to a FTP server. This is
+ simply achieved:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 1
+
+ In this case two FTP commands have been scheduled. When the last
+ scheduled command has finished, a done() signal is emitted with
+ a bool argument that tells you whether the sequence finished with
+ an error.
+
+ If an error occurs during the execution of one of the commands in
+ a sequence of commands, all the pending commands (i.e. scheduled,
+ but not yet executed commands) are cleared and no signals are
+ emitted for them.
+
+ Some commands, e.g. list(), emit additional signals to report
+ their results.
+
+ Example: If you want to download the INSTALL file from Trolltech's
+ FTP server, you would write this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 2
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 3
+
+ The dataTransferProgress() signal in the above example is useful
+ if you want to show a \link QProgressBar progress bar \endlink to
+ inform the user about the progress of the download. The
+ readyRead() signal tells you that there is data ready to be read.
+ The amount of data can be queried then with the bytesAvailable()
+ function and it can be read with the readBlock() or readAll()
+ function.
+
+ If the login fails for the above example, the signals would look
+ like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 4
+
+ You can then get details about the error with the error() and
+ errorString() functions.
+
+ The functions currentId() and currentCommand() provide more
+ information about the currently executing command.
+
+ The functions hasPendingCommands() and clearPendingCommands()
+ allow you to query and clear the list of pending commands.
+
+ The safest and easiest way to use the FTP protocol is to use
+ QUrlOperator() or the FTP commands described above. If you are an
+ experienced network programmer and want to have complete control
+ you can use rawCommand() to execute arbitrary FTP commands.
+
+ \sa Q3NetworkProtocol, Q3UrlOperator Q3Http
+*/
+
+/*!
+ Constructs a Q3Ftp object.
+*/
+Q3Ftp::Q3Ftp() : Q3NetworkProtocol()
+{
+ init();
+}
+
+/*!
+ Constructs a Q3Ftp object. The \a parent and \a name parameters
+ are passed to the QObject constructor.
+*/
+Q3Ftp::Q3Ftp( QObject *parent, const char *name ) : Q3NetworkProtocol()
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+}
+
+void Q3Ftp::init()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ d->errorString = QFtp::tr( "Unknown error" );
+
+ connect( &d->pi, SIGNAL(connectState(int)),
+ SLOT(piConnectState(int)) );
+ connect( &d->pi, SIGNAL(finished(QString)),
+ SLOT(piFinished(QString)) );
+ connect( &d->pi, SIGNAL(error(int,QString)),
+ SLOT(piError(int,QString)) );
+ connect( &d->pi, SIGNAL(rawFtpReply(int,QString)),
+ SLOT(piFtpReply(int,QString)) );
+
+ connect( &d->pi.dtp, SIGNAL(readyRead()),
+ SIGNAL(readyRead()) );
+ connect( &d->pi.dtp, SIGNAL(dataTransferProgress(int,int)),
+ SIGNAL(dataTransferProgress(int,int)) );
+ connect( &d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
+ SIGNAL(listInfo(QUrlInfo)) );
+}
+
+/*!
+ \enum Q3Ftp::State
+
+ This enum defines the connection state:
+
+ \value Unconnected There is no connection to the host.
+ \value HostLookup A host name lookup is in progress.
+ \value Connecting An attempt to connect to the host is in progress.
+ \value Connected Connection to the host has been achieved.
+ \value LoggedIn Connection and user login have been achieved.
+ \value Closing The connection is closing down, but it is not yet
+ closed. (The state will be \c Unconnected when the connection is
+ closed.)
+
+ \sa stateChanged() state()
+*/
+/*!
+ \enum Q3Ftp::Error
+
+ This enum identifies the error that occurred.
+
+ \value NoError No error occurred.
+ \value HostNotFound The host name lookup failed.
+ \value ConnectionRefused The server refused the connection.
+ \value NotConnected Tried to send a command, but there is no connection to
+ a server.
+ \value UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \enum Q3Ftp::Command
+
+ This enum is used as the return value for the currentCommand() function.
+ This allows you to perform specific actions for particular
+ commands, e.g. in a FTP client, you might want to clear the
+ directory view when a list() command is started; in this case you
+ can simply check in the slot connected to the start() signal if
+ the currentCommand() is \c List.
+
+ \value None No command is being executed.
+ \value ConnectToHost connectToHost() is being executed.
+ \value Login login() is being executed.
+ \value Close close() is being executed.
+ \value List list() is being executed.
+ \value Cd cd() is being executed.
+ \value Get get() is being executed.
+ \value Put put() is being executed.
+ \value Remove remove() is being executed.
+ \value Mkdir mkdir() is being executed.
+ \value Rmdir rmdir() is being executed.
+ \value Rename rename() is being executed.
+ \value RawCommand rawCommand() is being executed.
+
+ \sa currentCommand()
+*/
+
+/*!
+ \fn void Q3Ftp::stateChanged( int state )
+
+ This signal is emitted when the state of the connection changes.
+ The argument \a state is the new state of the connection; it is
+ one of the \l State values.
+
+ It is usually emitted in response to a connectToHost() or close()
+ command, but it can also be emitted "spontaneously", e.g. when the
+ server closes the connection unexpectedly.
+
+ \sa connectToHost() close() state() State
+*/
+
+/*!
+ \fn void Q3Ftp::listInfo( const QUrlInfo &i );
+
+ This signal is emitted for each directory entry the list() command
+ finds. The details of the entry are stored in \a i.
+
+ \sa list()
+*/
+
+/*!
+ \fn void Q3Ftp::commandStarted( int id )
+
+ This signal is emitted when processing the command identified by
+ \a id starts.
+
+ \sa commandFinished() done()
+*/
+
+/*!
+ \fn void Q3Ftp::commandFinished( int id, bool error )
+
+ This signal is emitted when processing the command identified by
+ \a id has finished. \a error is true if an error occurred during
+ the processing; otherwise \a error is false.
+
+ \sa commandStarted() done() error() errorString()
+*/
+
+/*!
+ \fn void Q3Ftp::done( bool error )
+
+ This signal is emitted when the last pending command has finished;
+ (it is emitted after the last command's commandFinished() signal).
+ \a error is true if an error occurred during the processing;
+ otherwise \a error is false.
+
+ \sa commandFinished() error() errorString()
+*/
+
+/*!
+ \fn void Q3Ftp::readyRead()
+
+ This signal is emitted in response to a get() command when there
+ is new data to read.
+
+ If you specify a device as the second argument in the get()
+ command, this signal is \e not emitted; instead the data is
+ written directly to the device.
+
+ You can read the data with the readAll() or readBlock() functions.
+
+ This signal is useful if you want to process the data in chunks as
+ soon as it becomes available. If you are only interested in the
+ complete data, just connect to the commandFinished() signal and
+ read the data then instead.
+
+ \sa get() readBlock() readAll() bytesAvailable()
+*/
+
+/*!
+ \fn void Q3Ftp::dataTransferProgress( int done, int total )
+
+ This signal is emitted in response to a get() or put() request to
+ indicate the current progress of the download or upload.
+
+ \a done is the amount of data that has already been transferred
+ and \a total is the total amount of data to be read or written. It
+ is possible that the Q3Ftp class is not able to determine the total
+ amount of data that should be transferred, in which case \a total
+ is 0. (If you connect this signal to a QProgressBar, the progress
+ bar shows a busy indicator if the total is 0).
+
+ \warning \a done and \a total are not necessarily the size in
+ bytes, since for large files these values might need to be
+ "scaled" to avoid overflow.
+
+ \sa get() put()
+*/
+
+/*!
+ \fn void Q3Ftp::rawCommandReply( int replyCode, const QString &detail );
+
+ This signal is emitted in response to the rawCommand() function.
+ \a replyCode is the 3 digit reply code and \a detail is the text
+ that follows the reply code.
+
+ \sa rawCommand()
+*/
+
+/*!
+ Connects to the FTP server \a host using port \a port.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c HostLookup, then \c
+ Connecting, then \c Connected.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa stateChanged() commandStarted() commandFinished()
+*/
+int Q3Ftp::connectToHost( const QString &host, Q_UINT16 port )
+{
+ QStringList cmds;
+ cmds << host;
+ cmds << QString::number( (uint)port );
+ return addCommand( new Q3FtpCommand( ConnectToHost, cmds ) );
+}
+
+/*!
+ Logs in to the FTP server with the username \a user and the
+ password \a password.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c LoggedIn.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::login( const QString &user, const QString &password )
+{
+ QStringList cmds;
+ cmds << ( QString(QLatin1String("USER ")) + ( user.isNull() ? QString(QLatin1String("anonymous")) : user ) + QLatin1String("\r\n") );
+ cmds << ( QString(QLatin1String("PASS ")) + ( password.isNull() ? QString(QLatin1String("anonymous@")) : password ) + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Login, cmds ) );
+}
+
+/*!
+ Closes the connection to the FTP server.
+
+ The stateChanged() signal is emitted when the state of the
+ connecting process changes, e.g. to \c Closing, then \c
+ Unconnected.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa stateChanged() commandStarted() commandFinished()
+*/
+int Q3Ftp::close()
+{
+ return addCommand( new Q3FtpCommand( Close, QStringList(QLatin1String("QUIT\r\n")) ) );
+}
+
+/*!
+ Lists the contents of directory \a dir on the FTP server. If \a
+ dir is empty, it lists the contents of the current directory.
+
+ The listInfo() signal is emitted for each directory entry found.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa listInfo() commandStarted() commandFinished()
+*/
+int Q3Ftp::list( const QString &dir )
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE A\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ if ( dir.isEmpty() )
+ cmds << QLatin1String("LIST\r\n");
+ else
+ cmds << ( QLatin1String("LIST ") + dir + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( List, cmds ) );
+}
+
+/*!
+ Changes the working directory of the server to \a dir.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::cd( const QString &dir )
+{
+ return addCommand( new Q3FtpCommand( Cd, QStringList(QLatin1String("CWD ")+dir+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Downloads the file \a file from the server.
+
+ If \a dev is 0, then the readyRead() signal is emitted when there
+ is data available to read. You can then read the data with the
+ readBlock() or readAll() functions.
+
+ If \a dev is not 0, the data is written directly to the device \a
+ dev. Make sure that the \a dev pointer is valid for the duration
+ of the operation (it is safe to delete it when the
+ commandFinished() signal is emitted). In this case the readyRead()
+ signal is \e not emitted and you cannot read data with the
+ readBlock() or readAll() functions.
+
+ If you don't read the data immediately it becomes available, i.e.
+ when the readyRead() signal is emitted, it is still available
+ until the next command is started.
+
+ For example, if you want to present the data to the user as soon
+ as there is something available, connect to the readyRead() signal
+ and read the data immediately. On the other hand, if you only want
+ to work with the complete data, you can connect to the
+ commandFinished() signal and read the data when the get() command
+ is finished.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa readyRead() dataTransferProgress() commandStarted()
+ commandFinished()
+*/
+int Q3Ftp::get( const QString &file, QIODevice *dev )
+{
+ QStringList cmds;
+ cmds << ( QLatin1String("SIZE ") + file + QLatin1String("\r\n") );
+ cmds << QLatin1String("TYPE I\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ cmds << ( QLatin1String("RETR ") + file + QLatin1String("\r\n") );
+ if ( dev )
+ return addCommand( new Q3FtpCommand( Get, cmds, dev ) );
+ return addCommand( new Q3FtpCommand( Get, cmds ) );
+}
+
+/*!
+ \overload
+
+ Writes the data \a data to the file called \a file on the server.
+ The progress of the upload is reported by the
+ dataTransferProgress() signal.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa dataTransferProgress() commandStarted() commandFinished()
+*/
+int Q3Ftp::put( const QByteArray &data, const QString &file )
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE I\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ cmds << ( QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n") );
+ cmds << ( QLatin1String("STOR ") + file + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Put, cmds, data ) );
+}
+
+/*!
+ Reads the data from the IO device \a dev, and writes it to the
+ file called \a file on the server. The data is read in chunks from
+ the IO device, so this overload allows you to transmit large
+ amounts of data without the need to read all the data into memory
+ at once.
+
+ Make sure that the \a dev pointer is valid for the duration of the
+ operation (it is safe to delete it when the commandFinished() is
+ emitted).
+*/
+int Q3Ftp::put( QIODevice *dev, const QString &file )
+{
+ QStringList cmds;
+ cmds << QLatin1String("TYPE I\r\n");
+ cmds << QLatin1String("PASV\r\n");
+ if ( !dev->isSequentialAccess() )
+ cmds << ( QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n") );
+ cmds << ( QLatin1String("STOR ") + file + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Put, cmds, dev ) );
+}
+
+/*!
+ Deletes the file called \a file from the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::remove( const QString &file )
+{
+ return addCommand( new Q3FtpCommand( Remove, QStringList(QLatin1String("DELE ")+file+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Creates a directory called \a dir on the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::mkdir( const QString &dir )
+{
+ return addCommand( new Q3FtpCommand( Mkdir, QStringList(QLatin1String("MKD ")+dir+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Removes the directory called \a dir from the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::rmdir( const QString &dir )
+{
+ return addCommand( new Q3FtpCommand( Rmdir, QStringList(QLatin1String("RMD ")+dir+QLatin1String("\r\n")) ) );
+}
+
+/*!
+ Renames the file called \a oldname to \a newname on the server.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa commandStarted() commandFinished()
+*/
+int Q3Ftp::rename( const QString &oldname, const QString &newname )
+{
+ QStringList cmds;
+ cmds << ( QLatin1String("RNFR ") + oldname + QLatin1String("\r\n") );
+ cmds << ( QLatin1String("RNTO ") + newname + QLatin1String("\r\n") );
+ return addCommand( new Q3FtpCommand( Rename, cmds ) );
+}
+
+/*!
+ Sends the raw FTP command \a command to the FTP server. This is
+ useful for low-level FTP access. If the operation you wish to
+ perform has an equivalent Q3Ftp function, we recommend using the
+ function instead of raw FTP commands since the functions are
+ easier and safer.
+
+ The function does not block and returns immediately. The command
+ is scheduled, and its execution is performed asynchronously. The
+ function returns a unique identifier which is passed by
+ commandStarted() and commandFinished().
+
+ When the command is started the commandStarted() signal is
+ emitted. When it is finished the commandFinished() signal is
+ emitted.
+
+ \sa rawCommandReply() commandStarted() commandFinished()
+*/
+int Q3Ftp::rawCommand( const QString &command )
+{
+ QString cmd = command.stripWhiteSpace() + QLatin1String("\r\n");
+ return addCommand( new Q3FtpCommand( RawCommand, QStringList(cmd) ) );
+}
+
+/*!
+ Returns the number of bytes that can be read from the data socket
+ at the moment.
+
+ \sa get() readyRead() readBlock() readAll()
+*/
+Q_ULONG Q3Ftp::bytesAvailable() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pi.dtp.bytesAvailable();
+}
+
+/*!
+ Reads \a maxlen bytes from the data socket into \a data and
+ returns the number of bytes read. Returns -1 if an error occurred.
+
+ \sa get() readyRead() bytesAvailable() readAll()
+*/
+Q_LONG Q3Ftp::readBlock( char *data, Q_ULONG maxlen )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pi.dtp.readBlock( data, maxlen );
+}
+
+/*!
+ Reads all the bytes available from the data socket and returns
+ them.
+
+ \sa get() readyRead() bytesAvailable() readBlock()
+*/
+QByteArray Q3Ftp::readAll()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pi.dtp.readAll();
+}
+
+/*!
+ Aborts the current command and deletes all scheduled commands.
+
+ If there is an unfinished command (i.e. a command for which the
+ commandStarted() signal has been emitted, but for which the
+ commandFinished() signal has not been emitted), this function
+ sends an \c ABORT command to the server. When the server replies
+ that the command is aborted, the commandFinished() signal with the
+ \c error argument set to \c true is emitted for the command. Due
+ to timing issues, it is possible that the command had already
+ finished before the abort request reached the server, in which
+ case, the commandFinished() signal is emitted with the \c error
+ argument set to \c false.
+
+ For all other commands that are affected by the abort(), no
+ signals are emitted.
+
+ If you don't start further FTP commands directly after the
+ abort(), there won't be any scheduled commands and the done()
+ signal is emitted.
+
+ \warning Some FTP servers, for example the BSD FTP daemon (version
+ 0.3), wrongly return a positive reply even when an abort has
+ occurred. For these servers the commandFinished() signal has its
+ error flag set to \c false, even though the command did not
+ complete successfully.
+
+ \sa clearPendingCommands()
+*/
+void Q3Ftp::abort()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ if ( d->pending.isEmpty() )
+ return;
+
+ clearPendingCommands();
+ d->pi.abort();
+}
+
+/*!
+ Returns the identifier of the FTP command that is being executed
+ or 0 if there is no command being executed.
+
+ \sa currentCommand()
+*/
+int Q3Ftp::currentId() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return 0;
+ return c->id;
+}
+
+/*!
+ Returns the command type of the FTP command being executed or \c
+ None if there is no command being executed.
+
+ \sa currentId()
+*/
+Q3Ftp::Command Q3Ftp::currentCommand() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return None;
+ return c->command;
+}
+
+/*!
+ Returns the QIODevice pointer that is used by the FTP command to read data
+ from or store data to. If there is no current FTP command being executed or
+ if the command does not use an IO device, this function returns 0.
+
+ This function can be used to delete the QIODevice in the slot connected to
+ the commandFinished() signal.
+
+ \sa get() put()
+*/
+QIODevice* Q3Ftp::currentDevice() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( !c )
+ return 0;
+ if ( c->is_ba )
+ return 0;
+ return c->data.dev;
+}
+
+/*!
+ Returns true if there are any commands scheduled that have not yet
+ been executed; otherwise returns false.
+
+ The command that is being executed is \e not considered as a
+ scheduled command.
+
+ \sa clearPendingCommands() currentId() currentCommand()
+*/
+bool Q3Ftp::hasPendingCommands() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->pending.count() > 1;
+}
+
+/*!
+ Deletes all pending commands from the list of scheduled commands.
+ This does not affect the command that is being executed. If you
+ want to stop this as well, use abort().
+
+ \sa hasPendingCommands() abort()
+*/
+void Q3Ftp::clearPendingCommands()
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = 0;
+ if ( d->pending.count() > 0 )
+ c = d->pending.take( 0 );
+ d->pending.clear();
+ if ( c )
+ d->pending.append( c );
+}
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+Q3Ftp::State Q3Ftp::state() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->state;
+}
+
+/*!
+ Returns the last error that occurred. This is useful to find out
+ what when wrong when receiving a commandFinished() or a done()
+ signal with the \c error argument set to \c true.
+
+ If you start a new command, the error status is reset to \c NoError.
+*/
+Q3Ftp::Error Q3Ftp::error() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->error;
+}
+
+/*!
+ Returns a human-readable description of the last error that
+ occurred. This is useful for presenting a error message to the
+ user when receiving a commandFinished() or a done() signal with
+ the \c error argument set to \c true.
+
+ The error string is often (but not always) the reply from the
+ server, so it is not always possible to translate the string. If
+ the message comes from Qt, the string has already passed through
+ tr().
+*/
+QString Q3Ftp::errorString() const
+{
+ Q3FtpPrivate *d = dHelper( this );
+ return d->errorString;
+}
+
+int Q3Ftp::addCommand( Q3FtpCommand *cmd )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ d->pending.append( cmd );
+
+ if ( d->pending.count() == 1 )
+ // don't emit the commandStarted() signal before the id is returned
+ QTimer::singleShot( 0, this, SLOT(startNextCommand()) );
+
+ return cmd->id;
+}
+
+void Q3Ftp::startNextCommand()
+{
+ Q3FtpPrivate *d = dHelper( this );
+
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return;
+
+ d->error = NoError;
+ d->errorString = QFtp::tr( "Unknown error" );
+
+ if ( bytesAvailable() )
+ readAll(); // clear the data
+ emit commandStarted( c->id );
+
+ if ( c->command == ConnectToHost ) {
+ d->pi.connectToHost( c->rawCmds[0], c->rawCmds[1].toUInt() );
+ } else {
+ if ( c->command == Put ) {
+ if ( c->is_ba ) {
+ d->pi.dtp.setData( c->data.ba );
+ d->pi.dtp.setBytesTotal( c->data.ba->size() );
+ } else if ( c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly)) ) {
+ d->pi.dtp.setDevice( c->data.dev );
+ if ( c->data.dev->isSequentialAccess() )
+ d->pi.dtp.setBytesTotal( 0 );
+ else
+ d->pi.dtp.setBytesTotal( c->data.dev->size() );
+ }
+ } else if ( c->command == Get ) {
+ if ( !c->is_ba && c->data.dev ) {
+ d->pi.dtp.setDevice( c->data.dev );
+ }
+ } else if ( c->command == Close ) {
+ d->state = Q3Ftp::Closing;
+ emit stateChanged( d->state );
+ }
+ if ( !d->pi.sendCommands( c->rawCmds ) ) {
+ // ### error handling (this case should not happen)
+ }
+ }
+}
+
+void Q3Ftp::piFinished( const QString& )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return;
+
+ if ( c->command == Close ) {
+ // The order of in which the slots are called is arbitrary, so
+ // disconnect the SIGNAL-SIGNAL temporary to make sure that we
+ // don't get the commandFinished() signal before the stateChanged()
+ // signal.
+ if ( d->state != Q3Ftp::Unconnected ) {
+ d->close_waitForStateChange = true;
+ return;
+ }
+ }
+ emit commandFinished( c->id, false );
+
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() ) {
+ emit done( false );
+ } else {
+ startNextCommand();
+ }
+}
+
+void Q3Ftp::piError( int errorCode, const QString &text )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ Q3FtpCommand *c = d->pending.getFirst();
+
+ // non-fatal errors
+ if ( c->command==Get && d->pi.currentCommand().startsWith(QLatin1String("SIZE ")) ) {
+ d->pi.dtp.setBytesTotal( -1 );
+ return;
+ } else if ( c->command==Put && d->pi.currentCommand().startsWith(QLatin1String("ALLO ")) ) {
+ return;
+ }
+
+ d->error = (Error)errorCode;
+ switch ( currentCommand() ) {
+ case ConnectToHost:
+ d->errorString = QFtp::tr( "Connecting to host failed:\n%1" ).arg( text );
+ break;
+ case Login:
+ d->errorString = QFtp::tr( "Login failed:\n%1" ).arg( text );
+ break;
+ case List:
+ d->errorString = QFtp::tr( "Listing directory failed:\n%1" ).arg( text );
+ break;
+ case Cd:
+ d->errorString = QFtp::tr( "Changing directory failed:\n%1" ).arg( text );
+ break;
+ case Get:
+ d->errorString = QFtp::tr( "Downloading file failed:\n%1" ).arg( text );
+ break;
+ case Put:
+ d->errorString = QFtp::tr( "Uploading file failed:\n%1" ).arg( text );
+ break;
+ case Remove:
+ d->errorString = QFtp::tr( "Removing file failed:\n%1" ).arg( text );
+ break;
+ case Mkdir:
+ d->errorString = QFtp::tr( "Creating directory failed:\n%1" ).arg( text );
+ break;
+ case Rmdir:
+ d->errorString = QFtp::tr( "Removing directory failed:\n%1" ).arg( text );
+ break;
+ default:
+ d->errorString = text;
+ break;
+ }
+
+ d->pi.clearPendingCommands();
+ clearPendingCommands();
+ emit commandFinished( c->id, true );
+
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() )
+ emit done( true );
+ else
+ startNextCommand();
+}
+
+void Q3Ftp::piConnectState( int state )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ d->state = (State)state;
+ emit stateChanged( d->state );
+ if ( d->close_waitForStateChange ) {
+ d->close_waitForStateChange = false;
+ piFinished( QFtp::tr( "Connection closed" ) );
+ }
+}
+
+void Q3Ftp::piFtpReply( int code, const QString &text )
+{
+ if ( currentCommand() == RawCommand ) {
+ Q3FtpPrivate *d = dHelper( this );
+ d->pi.rawCommand = true;
+ emit rawCommandReply( code, text );
+ }
+}
+
+/*!
+ Destructor.
+*/
+Q3Ftp::~Q3Ftp()
+{
+ abort();
+ close();
+ delete_d( this );
+}
+
+/**********************************************************************
+ *
+ * Q3Ftp implementation of the Q3NetworkProtocol interface
+ *
+ *********************************************************************/
+/*! \reimp
+*/
+void Q3Ftp::operationListChildren( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? QString( QLatin1String("/") ) : url()->path() ) );
+ list();
+ emit start( op );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationMkDir( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ mkdir( op->arg( 0 ) );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationRemove( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? QString( QLatin1String("/") ) : url()->path() ) );
+ remove( Q3Url( op->arg( 0 ) ).path() );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationRename( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? QString( QLatin1String("/") ) : url()->path() ) );
+ rename( op->arg( 0 ), op->arg( 1 ));
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationGet( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ Q3Url u( op->arg( 0 ) );
+ get( u.path() );
+}
+
+/*! \reimp
+*/
+void Q3Ftp::operationPut( Q3NetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ Q3Url u( op->arg( 0 ) );
+ put( op->rawArg(1), u.path() );
+}
+
+/*! \reimp
+*/
+bool Q3Ftp::checkConnection( Q3NetworkOperation *op )
+{
+ Q3FtpPrivate *d = dHelper( this );
+ if ( state() == Unconnected && !d->npWaitForLoginDone ) {
+ connect( this, SIGNAL(listInfo(QUrlInfo)),
+ this, SLOT(npListInfo(QUrlInfo)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(npDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(npStateChanged(int)) );
+ connect( this, SIGNAL(dataTransferProgress(int,int)),
+ this, SLOT(npDataTransferProgress(int,int)) );
+ connect( this, SIGNAL(readyRead()),
+ this, SLOT(npReadyRead()) );
+
+ d->npWaitForLoginDone = true;
+ switch ( op->operation() ) {
+ case OpGet:
+ case OpPut:
+ {
+ Q3Url u( op->arg( 0 ) );
+ connectToHost( u.host(), u.port() != -1 ? u.port() : 21 );
+ }
+ break;
+ default:
+ connectToHost( url()->host(), url()->port() != -1 ? url()->port() : 21 );
+ break;
+ }
+ QString user = url()->user().isEmpty() ? QString( QLatin1String("anonymous") ) : url()->user();
+ QString pass = url()->password().isEmpty() ? QString( QLatin1String("anonymous@") ) : url()->password();
+ login( user, pass );
+ }
+
+ if ( state() == LoggedIn )
+ return true;
+ return false;
+}
+
+/*! \reimp
+*/
+int Q3Ftp::supportedOperations() const
+{
+ return OpListChildren | OpMkDir | OpRemove | OpRename | OpGet | OpPut;
+}
+
+/*! \internal
+ Parses the string, \a buffer, which is one line of a directory
+ listing which came from the FTP server, and sets the values which
+ have been parsed to the url info object, \a info.
+*/
+void Q3Ftp::parseDir( const QString &buffer, QUrlInfo &info )
+{
+ Q3FtpDTP::parseDir( buffer, url()->user(), &info );
+}
+
+void Q3Ftp::npListInfo( const QUrlInfo & i )
+{
+ if ( url() ) {
+ QRegExp filt( url()->nameFilter(), false, true );
+ if ( i.isDir() || filt.search( i.name() ) != -1 ) {
+ emit newChild( i, operationInProgress() );
+ }
+ } else {
+ emit newChild( i, operationInProgress() );
+ }
+}
+
+void Q3Ftp::npDone( bool err )
+{
+ Q3FtpPrivate *d = dHelper( this );
+
+ bool emitFinishedSignal = false;
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( err ) {
+ op->setProtocolDetail( errorString() );
+ op->setState( StFailed );
+ if ( error() == HostNotFound ) {
+ op->setErrorCode( (int)ErrHostNotFound );
+ } else {
+ switch ( op->operation() ) {
+ case OpListChildren:
+ op->setErrorCode( (int)ErrListChildren );
+ break;
+ case OpMkDir:
+ op->setErrorCode( (int)ErrMkDir );
+ break;
+ case OpRemove:
+ op->setErrorCode( (int)ErrRemove );
+ break;
+ case OpRename:
+ op->setErrorCode( (int)ErrRename );
+ break;
+ case OpGet:
+ op->setErrorCode( (int)ErrGet );
+ break;
+ case OpPut:
+ op->setErrorCode( (int)ErrPut );
+ break;
+ }
+ }
+ emitFinishedSignal = true;
+ } else if ( !d->npWaitForLoginDone ) {
+ switch ( op->operation() ) {
+ case OpRemove:
+ emit removed( op );
+ break;
+ case OpMkDir:
+ {
+ QUrlInfo inf( op->arg( 0 ), 0, QLatin1String(""), QLatin1String(""), 0, QDateTime(),
+ QDateTime(), true, false, false, true, true, true );
+ emit newChild( inf, op );
+ emit createdDirectory( inf, op );
+ }
+ break;
+ case OpRename:
+ emit itemChanged( operationInProgress() );
+ break;
+ default:
+ break;
+ }
+ op->setState( StDone );
+ emitFinishedSignal = true;
+ }
+ }
+ d->npWaitForLoginDone = false;
+
+ if ( state() == Unconnected ) {
+ disconnect( this, SIGNAL(listInfo(QUrlInfo)),
+ this, SLOT(npListInfo(QUrlInfo)) );
+ disconnect( this, SIGNAL(done(bool)),
+ this, SLOT(npDone(bool)) );
+ disconnect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(npStateChanged(int)) );
+ disconnect( this, SIGNAL(dataTransferProgress(int,int)),
+ this, SLOT(npDataTransferProgress(int,int)) );
+ disconnect( this, SIGNAL(readyRead()),
+ this, SLOT(npReadyRead()) );
+ }
+
+ // emit the finished() signal at the very end to avoid reentrance problems
+ if ( emitFinishedSignal )
+ emit finished( op );
+}
+
+void Q3Ftp::npStateChanged( int state )
+{
+ if ( url() ) {
+ if ( state == Connecting )
+ emit connectionStateChanged( ConHostFound, QFtp::tr( "Host %1 found" ).arg( url()->host() ) );
+ else if ( state == Connected )
+ emit connectionStateChanged( ConConnected, QFtp::tr( "Connected to host %1" ).arg( url()->host() ) );
+ else if ( state == Unconnected )
+ emit connectionStateChanged( ConClosed, QFtp::tr( "Connection to %1 closed" ).arg( url()->host() ) );
+ } else {
+ if ( state == Connecting )
+ emit connectionStateChanged( ConHostFound, QFtp::tr( "Host found" ) );
+ else if ( state == Connected )
+ emit connectionStateChanged( ConConnected, QFtp::tr( "Connected to host" ) );
+ else if ( state == Unconnected )
+ emit connectionStateChanged( ConClosed, QFtp::tr( "Connection closed" ) );
+ }
+}
+
+void Q3Ftp::npDataTransferProgress( int bDone, int bTotal )
+{
+ emit Q3NetworkProtocol::dataTransferProgress( bDone, bTotal, operationInProgress() );
+}
+
+void Q3Ftp::npReadyRead()
+{
+ emit data( readAll(), operationInProgress() );
+}
+
+/*! \internal
+*/
+void Q3Ftp::hostFound()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::connected()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::closed()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataHostFound()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataConnected()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataClosed()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataReadyRead()
+{
+}
+/*! \internal
+*/
+void Q3Ftp::dataBytesWritten( int )
+{
+}
+/*! \internal
+*/
+void Q3Ftp::error( int )
+{
+}
+
+QT_END_NAMESPACE
+
+#include "q3ftp.moc"
+
+#endif // QT_NO_NETWORKPROTOCOL_FTP
diff --git a/src/qt3support/network/q3ftp.h b/src/qt3support/network/q3ftp.h
new file mode 100644
index 0000000000..685a6af382
--- /dev/null
+++ b/src/qt3support/network/q3ftp.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3FTP_H
+#define Q3FTP_H
+
+#include <QtCore/qstring.h> // char*->QString conversion
+#include <QtNetwork/qurlinfo.h>
+#include <Qt3Support/q3networkprotocol.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+
+class Q3Socket;
+class Q3FtpCommand;
+
+class Q_COMPAT_EXPORT Q3Ftp : public Q3NetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ Q3Ftp(); // ### Qt 4.0: get rid of this overload
+ Q3Ftp( QObject *parent, const char *name=0 );
+ virtual ~Q3Ftp();
+
+ int supportedOperations() const;
+
+ // non-Q3NetworkProtocol functions:
+ enum State {
+ Unconnected,
+ HostLookup,
+ Connecting,
+ Connected,
+ LoggedIn,
+ Closing
+ };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ NotConnected
+ };
+ enum Command {
+ None,
+ ConnectToHost,
+ Login,
+ Close,
+ List,
+ Cd,
+ Get,
+ Put,
+ Remove,
+ Mkdir,
+ Rmdir,
+ Rename,
+ RawCommand
+ };
+
+ int connectToHost( const QString &host, Q_UINT16 port=21 );
+ int login( const QString &user=QString(), const QString &password=QString() );
+ int close();
+ int list( const QString &dir=QString() );
+ int cd( const QString &dir );
+ int get( const QString &file, QIODevice *dev=0 );
+ int put( const QByteArray &data, const QString &file );
+ int put( QIODevice *dev, const QString &file );
+ int remove( const QString &file );
+ int mkdir( const QString &dir );
+ int rmdir( const QString &dir );
+ int rename( const QString &oldname, const QString &newname );
+
+ int rawCommand( const QString &command );
+
+ Q_ULONG bytesAvailable() const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice* currentDevice() const;
+ Command currentCommand() const;
+ bool hasPendingCommands() const;
+ void clearPendingCommands();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+Q_SIGNALS:
+ void stateChanged( int );
+ void listInfo( const QUrlInfo& );
+ void readyRead();
+ void dataTransferProgress( int, int );
+ void rawCommandReply( int, const QString& );
+
+ void commandStarted( int );
+ void commandFinished( int, bool );
+ void done( bool );
+
+protected:
+ void parseDir( const QString &buffer, QUrlInfo &info ); // ### Qt 4.0: delete this? (not public API)
+ void operationListChildren( Q3NetworkOperation *op );
+ void operationMkDir( Q3NetworkOperation *op );
+ void operationRemove( Q3NetworkOperation *op );
+ void operationRename( Q3NetworkOperation *op );
+ void operationGet( Q3NetworkOperation *op );
+ void operationPut( Q3NetworkOperation *op );
+
+ // ### Qt 4.0: delete these
+ // unused variables:
+ Q3Socket *commandSocket, *dataSocket;
+ bool connectionReady, passiveMode;
+ int getTotalSize, getDoneSize;
+ bool startGetOnFail;
+ int putToWrite, putWritten;
+ bool errorInListChildren;
+
+private:
+ void init();
+ int addCommand( Q3FtpCommand * );
+
+ bool checkConnection( Q3NetworkOperation *op );
+
+private Q_SLOTS:
+ void startNextCommand();
+ void piFinished( const QString& );
+ void piError( int, const QString& );
+ void piConnectState( int );
+ void piFtpReply( int, const QString& );
+
+private Q_SLOTS:
+ void npListInfo( const QUrlInfo & );
+ void npDone( bool );
+ void npStateChanged( int );
+ void npDataTransferProgress( int, int );
+ void npReadyRead();
+
+protected Q_SLOTS:
+ // ### Qt 4.0: delete these
+ void hostFound();
+ void connected();
+ void closed();
+ void dataHostFound();
+ void dataConnected();
+ void dataClosed();
+ void dataReadyRead();
+ void dataBytesWritten( int nbytes );
+ void error( int );
+};
+
+#endif // QT_NO_NETWORKPROTOCOL_FTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3FTP_H
diff --git a/src/qt3support/network/q3http.cpp b/src/qt3support/network/q3http.cpp
new file mode 100644
index 0000000000..f1590a6d28
--- /dev/null
+++ b/src/qt3support/network/q3http.cpp
@@ -0,0 +1,2322 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qplatformdefs.h>
+#include "q3http.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL_HTTP
+
+#include "q3socket.h"
+#include "qtextstream.h"
+#include "qmap.h"
+#include "qstring.h"
+#include "qstringlist.h"
+#include "q3cstring.h"
+#include "qbuffer.h"
+#include "q3urloperator.h"
+#include "qtimer.h"
+#include "private/q3membuf_p.h"
+#include "qevent.h"
+#include "q3url.h"
+#include "qhttp.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3HTTP_DEBUG
+
+class Q3HttpPrivate
+{
+public:
+ Q3HttpPrivate() :
+ state( Q3Http::Unconnected ),
+ error( Q3Http::NoError ),
+ hostname( QString() ),
+ port( 0 ),
+ toDevice( 0 ),
+ postDevice( 0 ),
+ bytesDone( 0 ),
+ chunkedSize( -1 ),
+ idleTimer( 0 )
+ {
+ pending.setAutoDelete( true );
+ }
+
+ Q3Socket socket;
+ Q3PtrList<Q3HttpRequest> pending;
+
+ Q3Http::State state;
+ Q3Http::Error error;
+ QString errorString;
+
+ QString hostname;
+ Q_UINT16 port;
+
+ QByteArray buffer;
+ QIODevice* toDevice;
+ QIODevice* postDevice;
+
+ uint bytesDone;
+ uint bytesTotal;
+ Q_LONG chunkedSize;
+
+ Q3HttpRequestHeader header;
+
+ bool readHeader;
+ QString headerStr;
+ Q3HttpResponseHeader response;
+
+ int idleTimer;
+
+ Q3Membuf rba;
+};
+
+class Q3HttpRequest
+{
+public:
+ Q3HttpRequest()
+ {
+ id = ++idCounter;
+ }
+ virtual ~Q3HttpRequest()
+ { }
+
+ virtual void start( Q3Http * ) = 0;
+ virtual bool hasRequestHeader();
+ virtual Q3HttpRequestHeader requestHeader();
+
+ virtual QIODevice* sourceDevice() = 0;
+ virtual QIODevice* destinationDevice() = 0;
+
+ int id;
+
+private:
+ static int idCounter;
+};
+
+int Q3HttpRequest::idCounter = 0;
+
+bool Q3HttpRequest::hasRequestHeader()
+{
+ return false;
+}
+
+Q3HttpRequestHeader Q3HttpRequest::requestHeader()
+{
+ return Q3HttpRequestHeader();
+}
+
+/****************************************************
+ *
+ * Q3HttpNormalRequest
+ *
+ ****************************************************/
+
+class Q3HttpNormalRequest : public Q3HttpRequest
+{
+public:
+ Q3HttpNormalRequest( const Q3HttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
+ header(h), to(t)
+ {
+ is_ba = false;
+ data.dev = d;
+ }
+
+ Q3HttpNormalRequest( const Q3HttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
+ header(h), to(t)
+ {
+ is_ba = true;
+ data.ba = d;
+ }
+
+ ~Q3HttpNormalRequest()
+ {
+ if ( is_ba )
+ delete data.ba;
+ }
+
+ void start( Q3Http * );
+ bool hasRequestHeader();
+ Q3HttpRequestHeader requestHeader();
+
+ QIODevice* sourceDevice();
+ QIODevice* destinationDevice();
+
+protected:
+ Q3HttpRequestHeader header;
+
+private:
+ union {
+ QByteArray *ba;
+ QIODevice *dev;
+ } data;
+ bool is_ba;
+ QIODevice *to;
+};
+
+void Q3HttpNormalRequest::start( Q3Http *http )
+{
+ http->d->header = header;
+
+ if ( is_ba ) {
+ http->d->buffer = *data.ba;
+ if ( http->d->buffer.size() > 0 )
+ http->d->header.setContentLength( http->d->buffer.size() );
+
+ http->d->postDevice = 0;
+ } else {
+ http->d->buffer = QByteArray();
+
+ if ( data.dev && ( data.dev->isOpen() || data.dev->open(IO_ReadOnly) ) ) {
+ http->d->postDevice = data.dev;
+ if ( http->d->postDevice->size() > 0 )
+ http->d->header.setContentLength( http->d->postDevice->size() );
+ } else {
+ http->d->postDevice = 0;
+ }
+ }
+
+ if ( to && ( to->isOpen() || to->open(IO_WriteOnly) ) )
+ http->d->toDevice = to;
+ else
+ http->d->toDevice = 0;
+
+ http->sendRequest();
+}
+
+bool Q3HttpNormalRequest::hasRequestHeader()
+{
+ return true;
+}
+
+Q3HttpRequestHeader Q3HttpNormalRequest::requestHeader()
+{
+ return header;
+}
+
+QIODevice* Q3HttpNormalRequest::sourceDevice()
+{
+ if ( is_ba )
+ return 0;
+ return data.dev;
+}
+
+QIODevice* Q3HttpNormalRequest::destinationDevice()
+{
+ return to;
+}
+
+/****************************************************
+ *
+ * Q3HttpPGHRequest
+ * (like a Q3HttpNormalRequest, but for the convenience
+ * functions put(), get() and head() -- i.e. set the
+ * host header field correctly before sending the
+ * request)
+ *
+ ****************************************************/
+
+class Q3HttpPGHRequest : public Q3HttpNormalRequest
+{
+public:
+ Q3HttpPGHRequest( const Q3HttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
+ Q3HttpNormalRequest( h, d, t )
+ { }
+
+ Q3HttpPGHRequest( const Q3HttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
+ Q3HttpNormalRequest( h, d, t )
+ { }
+
+ ~Q3HttpPGHRequest()
+ { }
+
+ void start( Q3Http * );
+};
+
+void Q3HttpPGHRequest::start( Q3Http *http )
+{
+ header.setValue( QLatin1String("Host"), http->d->hostname );
+ Q3HttpNormalRequest::start( http );
+}
+
+/****************************************************
+ *
+ * Q3HttpSetHostRequest
+ *
+ ****************************************************/
+
+class Q3HttpSetHostRequest : public Q3HttpRequest
+{
+public:
+ Q3HttpSetHostRequest( const QString &h, Q_UINT16 p ) :
+ hostname(h), port(p)
+ { }
+
+ void start( Q3Http * );
+
+ QIODevice* sourceDevice()
+ { return 0; }
+ QIODevice* destinationDevice()
+ { return 0; }
+
+private:
+ QString hostname;
+ Q_UINT16 port;
+};
+
+void Q3HttpSetHostRequest::start( Q3Http *http )
+{
+ http->d->hostname = hostname;
+ http->d->port = port;
+ http->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * Q3HttpCloseRequest
+ *
+ ****************************************************/
+
+class Q3HttpCloseRequest : public Q3HttpRequest
+{
+public:
+ Q3HttpCloseRequest()
+ { }
+ void start( Q3Http * );
+
+ QIODevice* sourceDevice()
+ { return 0; }
+ QIODevice* destinationDevice()
+ { return 0; }
+};
+
+void Q3HttpCloseRequest::start( Q3Http *http )
+{
+ http->close();
+}
+
+/****************************************************
+ *
+ * Q3HttpHeader
+ *
+ ****************************************************/
+
+/*!
+ \class Q3HttpHeader
+ \brief The Q3HttpHeader class contains header information for HTTP.
+
+ \compat
+
+ In most cases you should use the more specialized derivatives of
+ this class, Q3HttpResponseHeader and Q3HttpRequestHeader, rather
+ than directly using Q3HttpHeader.
+
+ Q3HttpHeader 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_qt3support_network_q3http.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_qt3support_network_q3http.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 Q3HttpRequestHeader Q3HttpResponseHeader
+*/
+
+/*!
+ \fn int Q3HttpHeader::majorVersion() const
+
+ Returns the major protocol-version of the HTTP header.
+*/
+
+/*!
+ \fn int Q3HttpHeader::minorVersion() const
+
+ Returns the minor protocol-version of the HTTP header.
+*/
+
+/*!
+ Constructs an empty HTTP header.
+*/
+Q3HttpHeader::Q3HttpHeader()
+ : valid( true )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+Q3HttpHeader::Q3HttpHeader( const Q3HttpHeader& header )
+ : valid( header.valid )
+{
+ values = header.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.
+*/
+Q3HttpHeader::Q3HttpHeader( const QString& str )
+ : valid( true )
+{
+ parse( str );
+}
+
+/*!
+ Destructor.
+*/
+Q3HttpHeader::~Q3HttpHeader()
+{
+}
+
+/*!
+ Assigns \a h and returns a reference to this http header.
+*/
+Q3HttpHeader& Q3HttpHeader::operator=( const Q3HttpHeader& h )
+{
+ values = h.values;
+ valid = h.valid;
+ return *this;
+}
+
+/*!
+ Returns true if the HTTP header is valid; otherwise returns false.
+
+ A Q3HttpHeader is invalid if it was created by parsing a malformed string.
+*/
+bool Q3HttpHeader::isValid() const
+{
+ return 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 Q3HttpHeader becomes \link isValid() invalid\endlink.
+
+ Returns true if \a str was successfully parsed; otherwise returns false.
+
+ \sa toString()
+*/
+bool Q3HttpHeader::parse( const QString& str )
+{
+ QStringList lst;
+ int pos = str.find( QLatin1Char('\n') );
+ if ( pos > 0 && str.at( pos - 1 ) == QLatin1Char('\r') )
+ lst = QStringList::split( QLatin1String("\r\n"), str.stripWhiteSpace(), false );
+ else
+ lst = QStringList::split( QLatin1String("\n"), str.stripWhiteSpace(), false );
+
+ 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() += QLatin1String(" ");
+ lines.last() += (*it).stripWhiteSpace();
+ }
+ } else {
+ lines.append( (*it) );
+ }
+ }
+ }
+
+ int number = 0;
+ it = lines.begin();
+ for( ; it != lines.end(); ++it ) {
+ if ( !parseLine( *it, number++ ) ) {
+ valid = false;
+ return false;
+ }
+ }
+ return true;
+}
+
+/*! \internal
+*/
+void Q3HttpHeader::setValid( bool v )
+{
+ valid = v;
+}
+
+/*!
+ Returns the 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 Q3HttpHeader::value( const QString& key ) const
+{
+ return values[ key.lower() ];
+}
+
+/*!
+ Returns a list of the keys in the HTTP header.
+
+ \sa hasKey()
+*/
+QStringList Q3HttpHeader::keys() const
+{
+ return values.keys();
+}
+
+/*!
+ Returns true if the HTTP header has an entry with the given \a
+ key; otherwise returns false.
+
+ \sa value() setValue() keys()
+*/
+bool Q3HttpHeader::hasKey( const QString& key ) const
+{
+ return values.contains( key.lower() );
+}
+
+/*!
+ 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, its value is discarded and replaced with the given \a
+ value.
+
+ \sa value() hasKey() removeValue()
+*/
+void Q3HttpHeader::setValue( const QString& key, const QString& value )
+{
+ values[ key.lower() ] = value;
+}
+
+/*!
+ Removes the entry with the key \a key from the HTTP header.
+
+ \sa value() setValue()
+*/
+void Q3HttpHeader::removeValue( const QString& key )
+{
+ values.remove( key.lower() );
+}
+
+/*! \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 Q3HttpHeader::parseLine( const QString& line, int )
+{
+ int i = line.find( QLatin1String(":") );
+ if ( i == -1 )
+ return false;
+
+ values.insert( line.left( i ).stripWhiteSpace().lower(), line.mid( i + 1 ).stripWhiteSpace() );
+
+ 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 Q3HttpHeader::toString() const
+{
+ if ( !isValid() )
+ return QLatin1String("");
+
+ QString ret = QLatin1String("");
+
+ QMap<QString,QString>::ConstIterator it = values.begin();
+ for( ; it != values.end(); ++it )
+ ret += it.key() + QLatin1String(": ") + it.data() + QLatin1String("\r\n");
+
+ 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 Q3HttpHeader::hasContentLength() const
+{
+ return hasKey( QLatin1String("content-length") );
+}
+
+/*!
+ Returns the value of the special HTTP header field \c
+ content-length.
+
+ \sa setContentLength() hasContentLength()
+*/
+uint Q3HttpHeader::contentLength() const
+{
+ return values[ QLatin1String("content-length") ].toUInt();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-length
+ to \a len.
+
+ \sa contentLength() hasContentLength()
+*/
+void Q3HttpHeader::setContentLength( int len )
+{
+ values[ QLatin1String("content-length") ] = QString::number( len );
+}
+
+/*!
+ Returns true if the header has an entry for the the special HTTP
+ header field \c content-type; otherwise returns false.
+
+ \sa contentType() setContentType()
+*/
+bool Q3HttpHeader::hasContentType() const
+{
+ return hasKey( QLatin1String("content-type") );
+}
+
+/*!
+ Returns the value of the special HTTP header field \c content-type.
+
+ \sa setContentType() hasContentType()
+*/
+QString Q3HttpHeader::contentType() const
+{
+ QString type = values[ QLatin1String("content-type") ];
+ if ( type.isEmpty() )
+ return QString();
+
+ int pos = type.find( QLatin1String(";") );
+ if ( pos == -1 )
+ return type;
+
+ return type.left( pos ).stripWhiteSpace();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-type to
+ \a type.
+
+ \sa contentType() hasContentType()
+*/
+void Q3HttpHeader::setContentType( const QString& type )
+{
+ values[ QLatin1String("content-type") ] = type;
+}
+
+/****************************************************
+ *
+ * Q3HttpResponseHeader
+ *
+ ****************************************************/
+
+/*!
+ \class Q3HttpResponseHeader
+ \brief The Q3HttpResponseHeader class contains response header information for HTTP.
+
+ \compat
+
+ This class is used by the Q3Http 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 Q3HttpRequestHeader Q3Http
+*/
+
+/*!
+ Constructs an empty HTTP response header.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader()
+{
+ setValid( false );
+}
+
+/*!
+ Constructs a HTTP response header with the status code \a code,
+ the reason phrase \a text and the protocol-version \a majorVer and
+ \a minorVer.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader( int code, const QString& text, int majorVer, int minorVer )
+ : Q3HttpHeader(), statCode( code ), reasonPhr( text ), majVer( majorVer ), minVer( minorVer )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader( const Q3HttpResponseHeader& header )
+ : Q3HttpHeader( header ), statCode( header.statCode ), reasonPhr( header.reasonPhr ), majVer( header.majVer ), minVer( header.minVer )
+{
+}
+
+/*!
+ 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.
+*/
+Q3HttpResponseHeader::Q3HttpResponseHeader( const QString& str )
+ : Q3HttpHeader()
+{
+ parse( str );
+}
+
+/*!
+ 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 Q3HttpResponseHeader::setStatusLine( int code, const QString& text, int majorVer, int minorVer )
+{
+ setValid( true );
+ statCode = code;
+ reasonPhr = text;
+ majVer = majorVer;
+ minVer = minorVer;
+}
+
+/*!
+ Returns the status code of the HTTP response header.
+
+ \sa reasonPhrase() majorVersion() minorVersion()
+*/
+int Q3HttpResponseHeader::statusCode() const
+{
+ return statCode;
+}
+
+/*!
+ Returns the reason phrase of the HTTP response header.
+
+ \sa statusCode() majorVersion() minorVersion()
+*/
+QString Q3HttpResponseHeader::reasonPhrase() const
+{
+ return reasonPhr;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP response header.
+
+ \sa minorVersion() statusCode() reasonPhrase()
+*/
+int Q3HttpResponseHeader::majorVersion() const
+{
+ return majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP response header.
+
+ \sa majorVersion() statusCode() reasonPhrase()
+*/
+int Q3HttpResponseHeader::minorVersion() const
+{
+ return minVer;
+}
+
+/*! \reimp
+*/
+bool Q3HttpResponseHeader::parseLine( const QString& line, int number )
+{
+ if ( number != 0 )
+ return Q3HttpHeader::parseLine( line, number );
+
+ QString l = line.simplifyWhiteSpace();
+ 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() ) {
+ majVer = l[5].latin1() - '0';
+ minVer = l[7].latin1() - '0';
+
+ int pos = l.find( QLatin1Char(' '), 9 );
+ if ( pos != -1 ) {
+ reasonPhr = l.mid( pos + 1 );
+ statCode = l.mid( 9, pos - 9 ).toInt();
+ } else {
+ statCode = l.mid( 9 ).toInt();
+ reasonPhr.clear();
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+/*! \reimp
+*/
+QString Q3HttpResponseHeader::toString() const
+{
+ QString ret( QLatin1String("HTTP/%1.%2 %3 %4\r\n%5\r\n") );
+ return ret.arg( majVer ).arg ( minVer ).arg( statCode ).arg( reasonPhr ).arg( Q3HttpHeader::toString() );
+}
+
+/****************************************************
+ *
+ * Q3HttpRequestHeader
+ *
+ ****************************************************/
+
+/*!
+ \class Q3HttpRequestHeader
+ \brief The Q3HttpRequestHeader class contains request header information for
+ HTTP.
+
+ \compat
+
+ This class is used in the Q3Http 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().
+
+ This class is a Q3HttpHeader subclass so that class's functions,
+ e.g. \link Q3HttpHeader::setValue() setValue()\endlink, \link
+ Q3HttpHeader::value() value()\endlink, etc. are also available.
+
+ \sa Q3HttpResponseHeader Q3Http
+*/
+
+/*!
+ Constructs an empty HTTP request header.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader()
+ : Q3HttpHeader()
+{
+ 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.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader( const QString& method, const QString& path, int majorVer, int minorVer )
+ : Q3HttpHeader(), m( method ), p( path ), majVer( majorVer ), minVer( minorVer )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader( const Q3HttpRequestHeader& header )
+ : Q3HttpHeader( header ), m( header.m ), p( header.p ), majVer( header.majVer ), minVer( header.minVer )
+{
+}
+
+/*!
+ 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.
+*/
+Q3HttpRequestHeader::Q3HttpRequestHeader( const QString& str )
+ : Q3HttpHeader()
+{
+ 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.
+
+ \sa method() path() majorVersion() minorVersion()
+*/
+void Q3HttpRequestHeader::setRequest( const QString& method, const QString& path, int majorVer, int minorVer )
+{
+ setValid( true );
+ m = method;
+ p = path;
+ majVer = majorVer;
+ minVer = minorVer;
+}
+
+/*!
+ Returns the method of the HTTP request header.
+
+ \sa path() majorVersion() minorVersion() setRequest()
+*/
+QString Q3HttpRequestHeader::method() const
+{
+ return m;
+}
+
+/*!
+ Returns the request-URI of the HTTP request header.
+
+ \sa method() majorVersion() minorVersion() setRequest()
+*/
+QString Q3HttpRequestHeader::path() const
+{
+ return p;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP request header.
+
+ \sa minorVersion() method() path() setRequest()
+*/
+int Q3HttpRequestHeader::majorVersion() const
+{
+ return majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP request header.
+
+ \sa majorVersion() method() path() setRequest()
+*/
+int Q3HttpRequestHeader::minorVersion() const
+{
+ return minVer;
+}
+
+/*! \reimp
+*/
+bool Q3HttpRequestHeader::parseLine( const QString& line, int number )
+{
+ if ( number != 0 )
+ return Q3HttpHeader::parseLine( line, number );
+
+ QStringList lst = QStringList::split( QLatin1String(" "), line.simplifyWhiteSpace() );
+ if ( lst.count() > 0 ) {
+ m = lst[0];
+ if ( lst.count() > 1 ) {
+ 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() ) {
+ majVer = v[5].latin1() - '0';
+ minVer = v[7].latin1() - '0';
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/*! \reimp
+*/
+QString Q3HttpRequestHeader::toString() const
+{
+ QString first( QLatin1String("%1 %2"));
+ QString last(QLatin1String(" HTTP/%3.%4\r\n%5\r\n") );
+ return first.arg( m ).arg( p ) +
+ last.arg( majVer ).arg( minVer ).arg( Q3HttpHeader::toString());
+}
+
+
+/****************************************************
+ *
+ * Q3Http
+ *
+ ****************************************************/
+/*!
+ \class Q3Http
+ \brief The Q3Http class provides an implementation of the HTTP protocol.
+
+ \compat
+
+ This class provides two different interfaces: one is the
+ Q3NetworkProtocol interface that allows you to use HTTP through the
+ QUrlOperator abstraction. The other is a direct interface to HTTP
+ that allows you to have more control over the requests and that
+ allows you to access the response header fields.
+
+ Don't mix the two interfaces, since the behavior is not
+ well-defined.
+
+ If you want to use Q3Http with the Q3NetworkProtocol interface, you
+ do not use it directly, but rather through a QUrlOperator, for
+ example:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 2
+
+ This code will only work if the Q3Http class is registered; to
+ register the class, you must call q3InitNetworkProtocols() before
+ using a QUrlOperator with HTTP.
+
+ The Q3NetworkProtocol interface for HTTP only supports the
+ operations operationGet() and operationPut(), i.e.
+ QUrlOperator::get() and QUrlOperator::put(), if you use it with a
+ QUrlOperator.
+
+ The rest of this descrption describes the direct interface to
+ HTTP.
+
+ The class works asynchronously, so there are no blocking
+ functions. If an operation cannot be executed immediately, the
+ function will still return straight away and the operation will be
+ scheduled for later execution. The results of scheduled operations
+ are reported via signals. This approach depends on the event loop
+ being in operation.
+
+ The operations that can be scheduled (they are called "requests"
+ in the rest of the documentation) are the following: setHost(),
+ get(), post(), head() and request().
+
+ All of these requests return a unique identifier that allows you
+ to keep track of the request that is currently executed. When the
+ execution of a request starts, the requestStarted() signal with
+ the identifier is emitted and when the request is finished, the
+ requestFinished() signal is emitted with the identifier and a bool
+ that indicates if the request finished with an error.
+
+ To make an HTTP request you must set up suitable HTTP headers. The
+ following example demonstrates, how to request the main HTML page
+ from the Trolltech home page (i.e. the URL
+ http://qtsoftware.com/index.html):
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 3
+
+ For the common HTTP requests \c GET, \c POST and \c HEAD, Q3Http
+ 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_qt3support_network_q3http.cpp 4
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 5
+
+ 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 readBlock()
+ 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 reqeusts
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3http.cpp 6
+
+ 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_qt3support_network_q3http.cpp 7
+
+ You can then get details about the error with the error() and
+ errorString() functions. Note that only unexpected behaviour, 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
+ Q3HttpResponseHeader::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 Q3NetworkProtocol, Q3UrlOperator, Q3Ftp
+*/
+
+/*!
+ Constructs a Q3Http object.
+*/
+Q3Http::Q3Http()
+{
+ init();
+}
+
+/*!
+ Constructs a Q3Http object. The parameters \a parent and \a name
+ are passed on to the QObject constructor.
+*/
+Q3Http::Q3Http( QObject* parent, const char* name )
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+}
+
+/*!
+ Constructs a Q3Http object. Subsequent requests are done by
+ connecting to the server \a hostname on port \a port. The
+ parameters \a parent and \a name are passed on to the QObject
+ constructor.
+
+ \sa setHost()
+*/
+Q3Http::Q3Http( const QString &hostname, Q_UINT16 port, QObject* parent, const char* name )
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+
+ d->hostname = hostname;
+ d->port = port;
+}
+
+void Q3Http::init()
+{
+ bytesRead = 0;
+ d = new Q3HttpPrivate;
+ d->errorString = QHttp::tr( "Unknown error" );
+
+ connect( &d->socket, SIGNAL(connected()),
+ this, SLOT(slotConnected()) );
+ connect( &d->socket, SIGNAL(connectionClosed()),
+ this, SLOT(slotClosed()) );
+ connect( &d->socket, SIGNAL(delayedCloseFinished()),
+ this, SLOT(slotClosed()) );
+ connect( &d->socket, SIGNAL(readyRead()),
+ this, SLOT(slotReadyRead()) );
+ connect( &d->socket, SIGNAL(error(int)),
+ this, SLOT(slotError(int)) );
+ connect( &d->socket, SIGNAL(bytesWritten(int)),
+ this, SLOT(slotBytesWritten(int)) );
+
+ d->idleTimer = startTimer( 0 );
+}
+
+/*!
+ Destroys the Q3Http object. If there is an open connection, it is
+ closed.
+*/
+Q3Http::~Q3Http()
+{
+ abort();
+ delete d;
+}
+
+/*!
+ \enum Q3Http::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 Q3Http::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 UnknownError An error other than those specified above
+ occurred.
+
+ \sa error()
+*/
+
+/*!
+ \fn void Q3Http::stateChanged( int state )
+
+ This signal is emitted when the state of the Q3Http 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
+ closeConnection() succeeded.
+
+ \sa get() post() head() request() closeConnection() state() State
+*/
+
+/*!
+ \fn void Q3Http::responseHeaderReceived( const Q3HttpResponseHeader& 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 Q3Http::readyRead( const Q3HttpResponseHeader& 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 readBlock() 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() readBlock() bytesAvailable()
+*/
+
+/*!
+ \fn void Q3Http::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::setValue()
+*/
+
+/*!
+ \fn void Q3Http::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::setValue()
+*/
+
+/*!
+ \fn void Q3Http::requestStarted( int id )
+
+ This signal is emitted when processing the request identified by
+ \a id starts.
+
+ \sa requestFinished() done()
+*/
+
+/*!
+ \fn void Q3Http::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 Q3Http::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()
+*/
+
+/*!
+ 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 Q3Http::abort()
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ finishedWithError( QHttp::tr("Request aborted"), Aborted );
+ clearPendingRequests();
+ d->socket.clearPendingData();
+ close();
+}
+
+/*!
+ Returns the number of bytes that can be read from the response
+ content at the moment.
+
+ \sa get() post() request() readyRead() readBlock() readAll()
+*/
+Q_ULONG Q3Http::bytesAvailable() const
+{
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::bytesAvailable(): %d bytes", (int)d->rba.size() );
+#endif
+ return d->rba.size();
+}
+
+/*!
+ 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()
+*/
+Q_LONG Q3Http::readBlock( char *data, Q_ULONG maxlen )
+{
+ if ( data == 0 && maxlen != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3Http::readBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( maxlen >= (Q_ULONG)d->rba.size() )
+ maxlen = d->rba.size();
+ d->rba.consumeBytes( maxlen, data );
+
+ d->bytesDone += maxlen;
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::readBlock(): read %d bytes (%d bytes done)", (int)maxlen, d->bytesDone );
+#endif
+ return maxlen;
+}
+
+/*!
+ Reads all the bytes from the response content and returns them.
+
+ \sa get() post() request() readyRead() bytesAvailable() readBlock()
+*/
+QByteArray Q3Http::readAll()
+{
+ Q_ULONG avail = bytesAvailable();
+ QByteArray tmp( avail );
+ Q_LONG read = readBlock( tmp.data(), avail );
+ tmp.resize( read );
+ 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 Q3Http::currentId() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return 0;
+ return r->id;
+}
+
+/*!
+ Returns the request header of the HTTP request being executed. If
+ the request is one issued by setHost() or closeConnection(), it
+ returns an invalid request header, i.e.
+ Q3HttpRequestHeader::isValid() returns false.
+
+ \sa currentId()
+*/
+Q3HttpRequestHeader Q3Http::currentRequest() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r != 0 && r->hasRequestHeader() )
+ return r->requestHeader();
+ return Q3HttpRequestHeader();
+}
+
+/*!
+ 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* Q3Http::currentSourceDevice() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( !r )
+ return 0;
+ return r->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 get() post() request()
+*/
+QIODevice* Q3Http::currentDestinationDevice() const
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( !r )
+ return 0;
+ return r->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 Q3Http::hasPendingRequests() const
+{
+ 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 Q3Http::clearPendingRequests()
+{
+ Q3HttpRequest *r = 0;
+ if ( d->pending.count() > 0 )
+ r = d->pending.take( 0 );
+ d->pending.clear();
+ if ( r )
+ d->pending.append( r );
+}
+
+/*!
+ Sets the HTTP server that is used for requests to \a hostname on
+ port \a port.
+
+ The function does not block and 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 Q3Http::setHost(const QString &hostname, Q_UINT16 port )
+{
+ return addRequest( new Q3HttpSetHostRequest( hostname, port ) );
+}
+
+/*!
+ Sends a get request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://qtsoftware.com/index.html.
+
+ 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 and 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 Q3Http::get( const QString& path, QIODevice* to )
+{
+ Q3HttpRequestHeader header( QLatin1String("GET"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, (QIODevice*)0, to ) );
+}
+
+/*!
+ Sends a post request for \a path to the server set by setHost() or
+ as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://qtsoftware.com/index.html.
+
+ 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 and 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 Q3Http::post( const QString& path, QIODevice* data, QIODevice* to )
+{
+ Q3HttpRequestHeader header( QLatin1String("POST"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, data, to ) );
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int Q3Http::post( const QString& path, const QByteArray& data, QIODevice* to )
+{
+ Q3HttpRequestHeader header( QLatin1String("POST"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( header, new QByteArray(data), to ) );
+}
+
+/*!
+ Sends a header request for \a path to the server set by setHost()
+ or as specified in the constructor.
+
+ \a path must be an absolute path like \c /index.html or an
+ absolute URI like \c http://qtsoftware.com/index.html.
+
+ The function does not block and 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 Q3Http::head( const QString& path )
+{
+ Q3HttpRequestHeader header( QLatin1String("HEAD"), path );
+ header.setValue( QLatin1String("Connection"), QLatin1String("Keep-Alive") );
+ return addRequest( new Q3HttpPGHRequest( 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 and 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 Q3Http::request( const Q3HttpRequestHeader &header, QIODevice *data, QIODevice *to )
+{
+ return addRequest( new Q3HttpNormalRequest( header, data, to ) );
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int Q3Http::request( const Q3HttpRequestHeader &header, const QByteArray &data, QIODevice *to )
+{
+ return addRequest( new Q3HttpNormalRequest( 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(), Q3Http sets
+ the connection to be keep-alive. You can also do this using the
+ header you pass to the request() function. Q3Http only closes the
+ connection to the HTTP server if the response header requires it
+ to do so.
+
+ The function does not block and 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 Q3Http::closeConnection()
+{
+ return addRequest( new Q3HttpCloseRequest() );
+}
+
+int Q3Http::addRequest( Q3HttpRequest *req )
+{
+ d->pending.append( req );
+
+ if ( d->pending.count() == 1 )
+ // don't emit the requestStarted() signal before the id is returned
+ QTimer::singleShot( 0, this, SLOT(startNextRequest()) );
+
+ return req->id;
+}
+
+void Q3Http::startNextRequest()
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ d->error = NoError;
+ d->errorString = QHttp::tr( "Unknown error" );
+
+ if ( bytesAvailable() )
+ readAll(); // clear the data
+ emit requestStarted( r->id );
+ r->start( this );
+}
+
+void Q3Http::sendRequest()
+{
+ if ( d->hostname.isNull() ) {
+ finishedWithError( QHttp::tr("No server set to connect to"), UnknownError );
+ return;
+ }
+
+ killIdleTimer();
+
+ // Do we need to setup a new connection or can we reuse an
+ // existing one ?
+ if ( d->socket.peerName() != d->hostname || d->socket.peerPort() != d->port
+ || d->socket.state() != Q3Socket::Connection ) {
+ setState( Q3Http::Connecting );
+ d->socket.connectToHost( d->hostname, d->port );
+ } else {
+ slotConnected();
+ }
+
+}
+
+void Q3Http::finishedWithSuccess()
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ emit requestFinished( r->id, false );
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() ) {
+ emit done( false );
+ } else {
+ startNextRequest();
+ }
+}
+
+void Q3Http::finishedWithError( const QString& detail, int errorCode )
+{
+ Q3HttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ d->error = (Error)errorCode;
+ d->errorString = detail;
+ emit requestFinished( r->id, true );
+
+ d->pending.clear();
+ emit done( true );
+}
+
+void Q3Http::slotClosed()
+{
+ if ( d->state == Closing )
+ return;
+
+ if ( d->state == Reading ) {
+ if ( d->response.hasKey( QLatin1String("content-length") ) ) {
+ // We got Content-Length, so did we get all bytes?
+ if ( d->bytesDone+bytesAvailable() != d->response.contentLength() ) {
+ finishedWithError( QHttp::tr("Wrong content length"), WrongContentLength );
+ }
+ }
+ } else if ( d->state == Connecting || d->state == Sending ) {
+ finishedWithError( QHttp::tr("Server closed connection unexpectedly"), UnexpectedClose );
+ }
+
+ d->postDevice = 0;
+ setState( Closing );
+ d->idleTimer = startTimer( 0 );
+}
+
+void Q3Http::slotConnected()
+{
+ if ( d->state != Sending ) {
+ d->bytesDone = 0;
+ setState( Sending );
+ }
+
+ QString str = d->header.toString();
+ d->bytesTotal = str.length();
+ d->socket.writeBlock( str.latin1(), d->bytesTotal );
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http: write request header:\n---{\n%s}---", str.latin1() );
+#endif
+
+ if ( d->postDevice ) {
+ d->bytesTotal += d->postDevice->size();
+ } else {
+ d->bytesTotal += d->buffer.size();
+ d->socket.writeBlock( d->buffer.data(), d->buffer.size() );
+ d->buffer = QByteArray(); // save memory
+ }
+}
+
+void Q3Http::slotError( int err )
+{
+ d->postDevice = 0;
+
+ if ( d->state == Connecting || d->state == Reading || d->state == Sending ) {
+ switch ( err ) {
+ case Q3Socket::ErrConnectionRefused:
+ finishedWithError( QHttp::tr("Connection refused"), ConnectionRefused );
+ break;
+ case Q3Socket::ErrHostNotFound:
+ finishedWithError( QHttp::tr("Host %1 not found").arg(d->socket.peerName()), HostNotFound );
+ break;
+ default:
+ finishedWithError( QHttp::tr("HTTP request failed"), UnknownError );
+ break;
+ }
+ }
+
+ close();
+}
+
+void Q3Http::slotBytesWritten( int written )
+{
+ d->bytesDone += written;
+ emit dataSendProgress( d->bytesDone, d->bytesTotal );
+
+ if ( !d->postDevice )
+ return;
+
+ if ( d->socket.bytesToWrite() == 0 ) {
+ int max = qMin<int>( 4096, d->postDevice->size() - d->postDevice->at() );
+ QByteArray arr( max );
+
+ int n = d->postDevice->readBlock( arr.data(), max );
+ if ( n != max ) {
+ qWarning("Could not read enough bytes from the device");
+ close();
+ return;
+ }
+ if ( d->postDevice->atEnd() ) {
+ d->postDevice = 0;
+ }
+
+ d->socket.writeBlock( arr.data(), max );
+ }
+}
+
+void Q3Http::slotReadyRead()
+{
+ if ( d->state != Reading ) {
+ setState( Reading );
+ d->buffer = QByteArray();
+ d->readHeader = true;
+ d->headerStr = QLatin1String("");
+ d->bytesDone = 0;
+ d->chunkedSize = -1;
+ }
+
+ while ( d->readHeader ) {
+ bool end = false;
+ QString tmp;
+ while ( !end && d->socket.canReadLine() ) {
+ tmp = QLatin1String(d->socket.readLine());
+ if ( tmp == QLatin1String("\r\n") || tmp == QLatin1String("\n") )
+ end = true;
+ else
+ d->headerStr += tmp;
+ }
+
+ if ( !end )
+ return;
+
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http: read response header:\n---{\n%s}---", d->headerStr.latin1() );
+#endif
+ d->response = Q3HttpResponseHeader( d->headerStr );
+ d->headerStr = QLatin1String("");
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http: read response header:\n---{\n%s}---", d->response.toString().latin1() );
+#endif
+ // Check header
+ if ( !d->response.isValid() ) {
+ finishedWithError( QHttp::tr("Invalid HTTP response header"), InvalidResponseHeader );
+ close();
+ return;
+ }
+
+ // The 100-continue header is ignored, because when using the
+ // POST method, we send both the request header and data in
+ // one chunk.
+ if (d->response.statusCode() != 100) {
+ d->readHeader = false;
+ if ( d->response.hasKey( QLatin1String("transfer-encoding") ) &&
+ d->response.value( QLatin1String("transfer-encoding") ).lower().contains( QLatin1String("chunked") ) )
+ d->chunkedSize = 0;
+
+ emit responseHeaderReceived( d->response );
+ }
+ }
+
+ if ( !d->readHeader ) {
+ bool everythingRead = false;
+
+ if ( currentRequest().method() == QLatin1String("HEAD") ) {
+ everythingRead = true;
+ } else {
+ Q_ULONG n = d->socket.bytesAvailable();
+ QByteArray *arr = 0;
+ if ( d->chunkedSize != -1 ) {
+ // transfer-encoding is chunked
+ for ( ;; ) {
+ // get chunk size
+ if ( d->chunkedSize == 0 ) {
+ if ( !d->socket.canReadLine() )
+ break;
+ QString sizeString = QLatin1String(d->socket.readLine());
+ int tPos = sizeString.find( QLatin1Char(';') );
+ if ( tPos != -1 )
+ sizeString.truncate( tPos );
+ bool ok;
+ d->chunkedSize = sizeString.toInt( &ok, 16 );
+ if ( !ok ) {
+ finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength );
+ close();
+ delete arr;
+ return;
+ }
+ if ( d->chunkedSize == 0 ) // last-chunk
+ d->chunkedSize = -2;
+ }
+
+ // read trailer
+ while ( d->chunkedSize == -2 && d->socket.canReadLine() ) {
+ QString read = QLatin1String(d->socket.readLine());
+ if ( read == QLatin1String("\r\n") || read == QLatin1String("\n") )
+ d->chunkedSize = -1;
+ }
+ if ( d->chunkedSize == -1 ) {
+ everythingRead = true;
+ break;
+ }
+
+ // make sure that you can read the terminating CRLF,
+ // otherwise wait until next time...
+ n = d->socket.bytesAvailable();
+ if ( n == 0 )
+ break;
+ if ( (Q_LONG)n == d->chunkedSize || (Q_LONG)n == d->chunkedSize+1 ) {
+ n = d->chunkedSize - 1;
+ if ( n == 0 )
+ break;
+ }
+
+ // read data
+ uint toRead = QMIN( (Q_LONG)n, (d->chunkedSize < 0 ? (Q_LONG)n : d->chunkedSize) );
+ if ( !arr )
+ arr = new QByteArray( 0 );
+ uint oldArrSize = arr->size();
+ arr->resize( oldArrSize + toRead );
+ Q_LONG read = d->socket.readBlock( arr->data()+oldArrSize, toRead );
+ arr->resize( oldArrSize + read );
+
+ d->chunkedSize -= read;
+
+ if ( d->chunkedSize == 0 && n - read >= 2 ) {
+ // read terminating CRLF
+ char tmp[2];
+ d->socket.readBlock( tmp, 2 );
+ if ( tmp[0] != '\r' || tmp[1] != '\n' ) {
+ finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength );
+ close();
+ delete arr;
+ return;
+ }
+ }
+ }
+ } else if ( d->response.hasContentLength() ) {
+ n = qMin<ulong>( d->response.contentLength() - d->bytesDone, n );
+ if ( n > 0 ) {
+ arr = new QByteArray( n );
+ Q_LONG read = d->socket.readBlock( arr->data(), n );
+ arr->resize( read );
+ }
+ if ( d->bytesDone + bytesAvailable() + n == d->response.contentLength() )
+ everythingRead = true;
+ } else if ( n > 0 ) {
+ // workaround for VC++ bug
+ QByteArray temp = d->socket.readAll();
+ arr = new QByteArray( temp );
+ }
+
+ if ( arr ) {
+ n = arr->size();
+ if ( d->toDevice ) {
+ d->toDevice->writeBlock( arr->data(), n );
+ delete arr;
+ d->bytesDone += n;
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%d bytes done)", n, d->bytesDone );
+#endif
+ if ( d->response.hasContentLength() )
+ emit dataReadProgress( d->bytesDone, d->response.contentLength() );
+ else
+ emit dataReadProgress( d->bytesDone, 0 );
+ } else {
+ d->rba.append( arr );
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%ld bytes done)", n, d->bytesDone + bytesAvailable() );
+#endif
+ if ( d->response.hasContentLength() )
+ emit dataReadProgress( d->bytesDone + bytesAvailable(), d->response.contentLength() );
+ else
+ emit dataReadProgress( d->bytesDone + bytesAvailable(), 0 );
+ emit readyRead( d->response );
+ }
+ }
+ }
+
+ if ( everythingRead ) {
+ // Handle "Connection: close"
+ if ( d->response.value(QLatin1String("connection")).lower() == QLatin1String("close") ) {
+ close();
+ } else {
+ setState( Connected );
+ // Start a timer, so that we emit the keep alive signal
+ // "after" this method returned.
+ d->idleTimer = startTimer( 0 );
+ }
+ }
+ }
+}
+
+/*!
+ Returns the current state of the object. When the state changes,
+ the stateChanged() signal is emitted.
+
+ \sa State stateChanged()
+*/
+Q3Http::State Q3Http::state() const
+{
+ 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.
+*/
+Q3Http::Error Q3Http::error() const
+{
+ 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 Q3Http::errorString() const
+{
+ return d->errorString;
+}
+
+/*! \reimp
+*/
+void Q3Http::timerEvent( QTimerEvent *e )
+{
+ if ( e->timerId() == d->idleTimer ) {
+ killTimer( d->idleTimer );
+ d->idleTimer = 0;
+
+ if ( d->state == Connected ) {
+ finishedWithSuccess();
+ } else if ( d->state != Unconnected ) {
+ setState( Unconnected );
+ finishedWithSuccess();
+ }
+ } else {
+ QObject::timerEvent( e );
+ }
+}
+
+void Q3Http::killIdleTimer()
+{
+ if (d->idleTimer)
+ killTimer( d->idleTimer );
+ d->idleTimer = 0;
+}
+
+void Q3Http::setState( int s )
+{
+#if defined(Q3HTTP_DEBUG)
+ qDebug( "Q3Http state changed %d -> %d", d->state, s );
+#endif
+ d->state = (State)s;
+ emit stateChanged( s );
+}
+
+void Q3Http::close()
+{
+ // If no connection is open -> ignore
+ if ( d->state == Closing || d->state == Unconnected )
+ return;
+
+ d->postDevice = 0;
+ setState( Closing );
+
+ // Already closed ?
+ if ( !d->socket.isOpen() ) {
+ d->idleTimer = startTimer( 0 );
+ } else {
+ // Close now.
+ d->socket.close();
+
+ // Did close succeed immediately ?
+ if ( d->socket.state() == Q3Socket::Idle ) {
+ // Prepare to emit the requestFinished() signal.
+ d->idleTimer = startTimer( 0 );
+ }
+ }
+}
+
+/**********************************************************************
+ *
+ * Q3Http implementation of the Q3NetworkProtocol interface
+ *
+ *********************************************************************/
+/*! \reimp
+*/
+int Q3Http::supportedOperations() const
+{
+ return OpGet | OpPut;
+}
+
+/*! \reimp
+*/
+void Q3Http::operationGet( Q3NetworkOperation *op )
+{
+ connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
+ this, SLOT(clientReply(Q3HttpResponseHeader)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ bytesRead = 0;
+ op->setState( StInProgress );
+ Q3Url u( operationInProgress()->arg( 0 ) );
+ Q3HttpRequestHeader header( QLatin1String("GET"), u.encodedPathAndQuery(), 1, 0 );
+ header.setValue( QLatin1String("Host"), u.host() );
+ setHost( u.host(), u.port() != -1 ? u.port() : 80 );
+ request( header );
+}
+
+/*! \reimp
+*/
+void Q3Http::operationPut( Q3NetworkOperation *op )
+{
+ connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
+ this, SLOT(clientReply(Q3HttpResponseHeader)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ bytesRead = 0;
+ op->setState( StInProgress );
+ Q3Url u( operationInProgress()->arg( 0 ) );
+ Q3HttpRequestHeader header( QLatin1String("POST"), u.encodedPathAndQuery(), 1, 0 );
+ header.setValue( QLatin1String("Host"), u.host() );
+ setHost( u.host(), u.port() != -1 ? u.port() : 80 );
+ request( header, op->rawArg(1) );
+}
+
+void Q3Http::clientReply( const Q3HttpResponseHeader &rep )
+{
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( rep.statusCode() >= 400 && rep.statusCode() < 600 ) {
+ op->setState( StFailed );
+ op->setProtocolDetail(
+ QString(QLatin1String("%1 %2")).arg(rep.statusCode()).arg(rep.reasonPhrase())
+ );
+ switch ( rep.statusCode() ) {
+ case 401:
+ case 403:
+ case 405:
+ op->setErrorCode( ErrPermissionDenied );
+ break;
+ case 404:
+ op->setErrorCode(ErrFileNotExisting );
+ break;
+ default:
+ if ( op->operation() == OpGet )
+ op->setErrorCode( ErrGet );
+ else
+ op->setErrorCode( ErrPut );
+ break;
+ }
+ }
+ // ### In cases of an error, should we still emit the data() signals?
+ if ( op->operation() == OpGet && bytesAvailable() > 0 ) {
+ QByteArray ba = readAll();
+ emit data( ba, op );
+ bytesRead += ba.size();
+ if ( rep.hasContentLength() ) {
+ emit dataTransferProgress( bytesRead, rep.contentLength(), op );
+ }
+ }
+ }
+}
+
+void Q3Http::clientDone( bool err )
+{
+ disconnect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
+ this, SLOT(clientReply(Q3HttpResponseHeader)) );
+ disconnect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ disconnect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ if ( err ) {
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ op->setState( Q3NetworkProtocol::StFailed );
+ op->setProtocolDetail( errorString() );
+ switch ( error() ) {
+ case ConnectionRefused:
+ op->setErrorCode( ErrHostNotFound );
+ break;
+ case HostNotFound:
+ op->setErrorCode( ErrHostNotFound );
+ break;
+ default:
+ if ( op->operation() == OpGet )
+ op->setErrorCode( ErrGet );
+ else
+ op->setErrorCode( ErrPut );
+ break;
+ }
+ emit finished( op );
+ }
+ } else {
+ Q3NetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( op->state() != StFailed ) {
+ op->setState( Q3NetworkProtocol::StDone );
+ op->setErrorCode( Q3NetworkProtocol::NoError );
+ }
+ emit finished( op );
+ }
+ }
+
+}
+
+void Q3Http::clientStateChanged( int state )
+{
+ if ( url() ) {
+ switch ( (State)state ) {
+ case Connecting:
+ emit connectionStateChanged( ConHostFound, QHttp::tr( "Host %1 found" ).arg( url()->host() ) );
+ break;
+ case Sending:
+ emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host %1" ).arg( url()->host() ) );
+ break;
+ case Unconnected:
+ emit connectionStateChanged( ConClosed, QHttp::tr( "Connection to %1 closed" ).arg( url()->host() ) );
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch ( (State)state ) {
+ case Connecting:
+ emit connectionStateChanged( ConHostFound, QHttp::tr( "Host found" ) );
+ break;
+ case Sending:
+ emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host" ) );
+ break;
+ case Unconnected:
+ emit connectionStateChanged( ConClosed, QHttp::tr( "Connection closed" ) );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/network/q3http.h b/src/qt3support/network/q3http.h
new file mode 100644
index 0000000000..95be15d9ec
--- /dev/null
+++ b/src/qt3support/network/q3http.h
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3HTTP_H
+#define Q3HTTP_H
+
+#include <QtCore/qobject.h>
+#include <Qt3Support/q3networkprotocol.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORKPROTOCOL_HTTP
+
+class Q3Socket;
+class QTimerEvent;
+class QTextStream;
+class QIODevice;
+
+class Q3HttpPrivate;
+class Q3HttpRequest;
+
+class Q_COMPAT_EXPORT Q3HttpHeader
+{
+public:
+ Q3HttpHeader();
+ Q3HttpHeader( const Q3HttpHeader& header );
+ Q3HttpHeader( const QString& str );
+ virtual ~Q3HttpHeader();
+
+ Q3HttpHeader& operator=( const Q3HttpHeader& h );
+
+ QString value( const QString& key ) const;
+ void setValue( const QString& key, const QString& value );
+ void removeValue( const QString& key );
+
+ QStringList keys() const;
+ bool hasKey( const QString& key ) const;
+
+ 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 );
+
+private:
+ QMap<QString,QString> values;
+ bool valid;
+};
+
+class Q_COMPAT_EXPORT Q3HttpResponseHeader : public Q3HttpHeader
+{
+private:
+ Q3HttpResponseHeader( int code, const QString& text = QString(), int majorVer = 1, int minorVer = 1 );
+ Q3HttpResponseHeader( const QString& str );
+
+ void setStatusLine( int code, const QString& text = QString(), int majorVer = 1, int minorVer = 1 );
+
+public:
+ Q3HttpResponseHeader();
+ Q3HttpResponseHeader( const Q3HttpResponseHeader& header );
+
+ int statusCode() const;
+ QString reasonPhrase() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ QString toString() const;
+
+protected:
+ bool parseLine( const QString& line, int number );
+
+private:
+ int statCode;
+ QString reasonPhr;
+ int majVer;
+ int minVer;
+
+ friend class Q3Http;
+};
+
+class Q_COMPAT_EXPORT Q3HttpRequestHeader : public Q3HttpHeader
+{
+public:
+ Q3HttpRequestHeader();
+ Q3HttpRequestHeader( const QString& method, const QString& path, int majorVer = 1, int minorVer = 1 );
+ Q3HttpRequestHeader( const Q3HttpRequestHeader& header );
+ Q3HttpRequestHeader( const QString& str );
+
+ 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:
+ QString m;
+ QString p;
+ int majVer;
+ int minVer;
+};
+
+class Q_COMPAT_EXPORT Q3Http : public Q3NetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ Q3Http();
+ Q3Http( QObject* parent, const char* name = 0 ); // ### Qt 4.0: make parent=0 and get rid of the Q3Http() constructor
+ Q3Http( const QString &hostname, Q_UINT16 port=80, QObject* parent=0, const char* name = 0 );
+ virtual ~Q3Http();
+
+ int supportedOperations() const;
+
+ enum State { Unconnected, HostLookup, Connecting, Sending, Reading, Connected, Closing };
+ enum Error {
+ NoError,
+ UnknownError,
+ HostNotFound,
+ ConnectionRefused,
+ UnexpectedClose,
+ InvalidResponseHeader,
+ WrongContentLength,
+ Aborted
+ };
+
+ int setHost(const QString &hostname, Q_UINT16 port=80 );
+
+ 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 Q3HttpRequestHeader &header, QIODevice *device=0, QIODevice *to=0 );
+ int request( const Q3HttpRequestHeader &header, const QByteArray &data, QIODevice *to=0 );
+
+ int closeConnection();
+
+ Q_ULONG bytesAvailable() const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ QByteArray readAll();
+
+ int currentId() const;
+ QIODevice* currentSourceDevice() const;
+ QIODevice* currentDestinationDevice() const;
+ Q3HttpRequestHeader currentRequest() const;
+ bool hasPendingRequests() const;
+ void clearPendingRequests();
+
+ State state() const;
+
+ Error error() const;
+ QString errorString() const;
+
+public Q_SLOTS:
+ void abort();
+
+Q_SIGNALS:
+ void stateChanged( int );
+ void responseHeaderReceived( const Q3HttpResponseHeader& resp );
+ void readyRead( const Q3HttpResponseHeader& resp );
+ void dataSendProgress( int, int );
+ void dataReadProgress( int, int );
+
+ void requestStarted( int );
+ void requestFinished( int, bool );
+ void done( bool );
+
+protected:
+ void operationGet( Q3NetworkOperation *op );
+ void operationPut( Q3NetworkOperation *op );
+
+ void timerEvent( QTimerEvent * );
+
+private Q_SLOTS:
+ void clientReply( const Q3HttpResponseHeader &rep );
+ void clientDone( bool );
+ void clientStateChanged( int );
+
+ void startNextRequest();
+ void slotReadyRead();
+ void slotConnected();
+ void slotError( int );
+ void slotClosed();
+ void slotBytesWritten( int );
+
+private:
+ Q3HttpPrivate *d;
+ void *unused; // ### Qt 4.0: remove this (in for binary compatibility)
+ int bytesRead;
+
+ int addRequest( Q3HttpRequest * );
+ void sendRequest();
+ void finishedWithSuccess();
+ void finishedWithError( const QString& detail, int errorCode );
+
+ void killIdleTimer();
+
+ void init();
+ void setState( int );
+ void close();
+
+ friend class Q3HttpNormalRequest;
+ friend class Q3HttpSetHostRequest;
+ friend class Q3HttpCloseRequest;
+ friend class Q3HttpPGHRequest;
+};
+
+#endif // QT_NO_NETWORKPROTOCOL_HTTP
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3HTTP_H
diff --git a/src/qt3support/network/q3localfs.cpp b/src/qt3support/network/q3localfs.cpp
new file mode 100644
index 0000000000..619d4c28a1
--- /dev/null
+++ b/src/qt3support/network/q3localfs.cpp
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3localfs.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "qfileinfo.h"
+#include "qfile.h"
+#include "q3url.h"
+#include "qurlinfo.h"
+#include "qapplication.h"
+#include "q3urloperator.h"
+#include "qpointer.h"
+#include "q3valuelist.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define QLOCALFS_DEBUG
+
+
+/*!
+ \class Q3LocalFs
+ \brief The Q3LocalFs class is an implementation of a
+ QNetworkProtocol that works on the local file system.
+
+ \compat
+
+ This class is derived from QNetworkProtocol. Q3LocalFs is not
+ normally used directly, but rather through a QUrlOperator, for
+ example:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3localfs.cpp 0
+
+ This code will only work if the Q3LocalFs class is registered; to
+ register the class, you must call qInitNetworkProtocols() before
+ using a QUrlOperator with Q3LocalFs.
+
+ If you really need to use Q3LocalFs directly, don't forget
+ to set its QUrlOperator with setUrl().
+
+ \sa Q3NetworkProtocol, Q3UrlOperator
+*/
+
+/*!
+ Constructor.
+*/
+
+Q3LocalFs::Q3LocalFs()
+ : Q3NetworkProtocol()
+{
+}
+
+static int convertPermissions(QFileInfo *fi)
+{
+ int p = 0;
+ if ( fi->permission( QFileInfo::ReadOwner ) )
+ p |= QUrlInfo::ReadOwner;
+ if ( fi->permission( QFileInfo::WriteOwner ) )
+ p |= QUrlInfo::WriteOwner;
+ if ( fi->permission( QFileInfo::ExeOwner ) )
+ p |= QUrlInfo::ExeOwner;
+ if ( fi->permission( QFileInfo::ReadGroup ) )
+ p |= QUrlInfo::ReadGroup;
+ if ( fi->permission( QFileInfo::WriteGroup ) )
+ p |= QUrlInfo::WriteGroup;
+ if ( fi->permission( QFileInfo::ExeGroup ) )
+ p |= QUrlInfo::ExeGroup;
+ if ( fi->permission( QFileInfo::ReadOther ) )
+ p |= QUrlInfo::ReadOther;
+ if ( fi->permission( QFileInfo::WriteOther ) )
+ p |= QUrlInfo::WriteOther;
+ if ( fi->permission( QFileInfo::ExeOther ) )
+ p |= QUrlInfo::ExeOther;
+ return p;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationListChildren( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationListChildren" );
+#endif
+ op->setState( StInProgress );
+
+ dir = QDir( url()->path() );
+ dir.setNameFilter( url()->nameFilter() );
+ dir.setMatchAllDirs( true );
+ if ( !dir.isReadable() ) {
+ QString msg = tr( "Could not read directory\n%1" ).arg( url()->path() );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrListChildren );
+ emit finished( op );
+ return;
+ }
+
+ QFileInfoList filist = dir.entryInfoList(QDir::All | QDir::Hidden | QDir::System);
+ if ( filist.isEmpty() ) {
+ QString msg = tr( "Could not read directory\n%1" ).arg( url()->path() );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrListChildren );
+ emit finished( op );
+ return;
+ }
+
+ emit start( op );
+
+ Q3ValueList<QUrlInfo> infos;
+ for (int i = 0; i < filist.size(); ++i) {
+ QFileInfo fi = filist.at(i);
+ infos << QUrlInfo( fi.fileName(), convertPermissions(&fi), fi.owner(), fi.group(),
+ fi.size(), fi.lastModified(), fi.lastRead(), fi.isDir(), fi.isFile(),
+ fi.isSymLink(), fi.isWritable(), fi.isReadable(), fi.isExecutable() );
+ }
+ emit newChildren( infos, op );
+ op->setState( StDone );
+ emit finished( op );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationMkDir( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationMkDir" );
+#endif
+ op->setState( StInProgress );
+ QString dirname = op->arg( 0 );
+
+ dir = QDir( url()->path() );
+ if ( dir.mkdir( dirname ) ) {
+ QFileInfo fi( dir, dirname );
+ QUrlInfo inf( fi.fileName(), convertPermissions(&fi), fi.owner(), fi.group(),
+ fi.size(), fi.lastModified(), fi.lastRead(), fi.isDir(), fi.isFile(),
+ fi.isSymLink(), fi.isWritable(), fi.isReadable(), fi.isExecutable() );
+ emit newChild( inf, op );
+ op->setState( StDone );
+ emit createdDirectory( inf, op );
+ emit finished( op );
+ } else {
+ QString msg = tr( "Could not create directory\n%1" ).arg( dirname );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrMkDir );
+ emit finished( op );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationRemove( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationRemove" );
+#endif
+ op->setState( StInProgress );
+ QString name = Q3Url( op->arg( 0 ) ).path();
+ bool deleted = false;
+
+ dir = QDir( url()->path() );
+
+ QFileInfo fi( dir, name );
+ if ( fi.isDir() ) {
+ if ( dir.rmdir( name ) )
+ deleted = true;
+ }
+
+ if ( deleted || dir.remove( name ) ) {
+ op->setState( StDone );
+ emit removed( op );
+ emit finished( op );
+ } else {
+ QString msg = tr( "Could not remove file or directory\n%1" ).arg( name );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrRemove );
+ emit finished( op );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationRename( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationRename" );
+#endif
+ op->setState( StInProgress );
+ QString oldname = op->arg( 0 );
+ QString newname = op->arg( 1 );
+
+ dir = QDir( url()->path() );
+ if ( dir.rename( oldname, newname ) ) {
+ op->setState( StDone );
+ emit itemChanged( op );
+ emit finished( op );
+ } else {
+ QString msg = tr( "Could not rename\n%1\nto\n%2" ).arg( oldname ).arg( newname );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrRename );
+ emit finished( op );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationGet( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationGet" );
+#endif
+ op->setState( StInProgress );
+ QString from = Q3Url( op->arg( 0 ) ).path();
+
+ QFile f( from );
+ if ( !f.open( IO_ReadOnly ) ) {
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: could not open %s", from.latin1() );
+#endif
+ QString msg = tr( "Could not open\n%1" ).arg( from );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrGet );
+ emit finished( op );
+ return;
+ }
+
+ QByteArray s;
+ emit dataTransferProgress( 0, f.size(), op );
+ if ( f.size() != 0 ) {
+ int blockSize = calcBlockSize( f.size() );
+ if ( (int)f.size() < blockSize ) {
+ s.resize( f.size() );
+ f.readBlock( s.data(), f.size() );
+ emit data( s, op );
+ emit dataTransferProgress( f.size(), f.size(), op );
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: got all %d bytes at once", f.size() );
+#endif
+ } else {
+ s.resize( blockSize );
+ int remaining = f.size();
+ QPointer<QObject> that = this;
+ while ( that && remaining > 0 ) {
+ if ( operationInProgress() != op )
+ return;
+ if ( remaining >= blockSize ) {
+ f.readBlock( s.data(), blockSize );
+ emit data( s, op );
+ emit dataTransferProgress( f.size() - remaining, f.size(), op );
+ remaining -= blockSize;
+ } else {
+ s.resize( remaining );
+ f.readBlock( s.data(), remaining );
+ emit data( s, op );
+ emit dataTransferProgress( f.size() - remaining, f.size(), op );
+ remaining -= remaining;
+ }
+ qApp->processEvents();
+ }
+ if ( !that )
+ return;
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: got all %d bytes step by step", f.size() );
+#endif
+ emit dataTransferProgress( f.size(), f.size(), op );
+ }
+ }
+ op->setState( StDone );
+ f.close();
+ emit finished( op );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3LocalFs::operationPut( Q3NetworkOperation *op )
+{
+#ifdef QLOCALFS_DEBUG
+ qDebug( "Q3LocalFs: operationPut" );
+#endif
+ op->setState( StInProgress );
+ QString to = Q3Url( op->arg( 0 ) ).path();
+
+ QFile f( to );
+ if ( !f.open( IO_WriteOnly ) ) {
+ QString msg = tr( "Could not write\n%1" ).arg( to );
+ op->setState( StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)ErrPut );
+ emit finished( op );
+ return;
+ }
+
+ QByteArray ba( op->rawArg( 1 ) );
+ emit dataTransferProgress( 0, ba.size(), op );
+ int blockSize = calcBlockSize( ba.size() );
+ if ( (int)ba.size() < blockSize ) {
+ f.writeBlock( ba.data(), ba.size() );
+ emit dataTransferProgress( ba.size(), ba.size(), op );
+ } else {
+ int i = 0;
+ while ( i + blockSize < (int)ba.size() - 1 ) {
+ if ( operationInProgress() != op )
+ return;
+ f.writeBlock( &ba.data()[ i ], blockSize );
+ f.flush();
+ emit dataTransferProgress( i + blockSize, ba.size(), op );
+ i += blockSize;
+ QPointer<QObject> that = this;
+ qApp->processEvents();
+ if (!that)
+ return;
+ }
+ if ( i < (int)ba.size() - 1 )
+ f.writeBlock( &ba.data()[ i ], ba.size() - i );
+ emit dataTransferProgress( ba.size(), ba.size(), op );
+ }
+ op->setState( StDone );
+ f.close();
+ emit finished( op );
+}
+
+/*!
+ \reimp
+*/
+
+int Q3LocalFs::supportedOperations() const
+{
+ return OpListChildren | OpMkDir | OpRemove | OpRename | OpGet | OpPut;
+}
+
+/*!
+ \internal
+*/
+
+int Q3LocalFs::calcBlockSize( int totalSize ) const
+{
+ if ( totalSize == 0 )
+ return 1024;
+ int s = totalSize / 100;
+ // we want a block size between 1KB and 1MB
+ if ( s < 1024 )
+ s = 1024;
+ if ( s > 1048576 )
+ s = 1048576;
+ return s;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORKPROTOCOL
diff --git a/src/qt3support/network/q3localfs.h b/src/qt3support/network/q3localfs.h
new file mode 100644
index 0000000000..9ce4f8a114
--- /dev/null
+++ b/src/qt3support/network/q3localfs.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3LOCALFS_H
+#define Q3LOCALFS_H
+
+#include <Qt3Support/q3networkprotocol.h>
+#include <QtCore/qdir.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+class Q_COMPAT_EXPORT Q3LocalFs : public Q3NetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ Q3LocalFs();
+ virtual int supportedOperations() const;
+
+protected:
+ virtual void operationListChildren( Q3NetworkOperation *op );
+ virtual void operationMkDir( Q3NetworkOperation *op );
+ virtual void operationRemove( Q3NetworkOperation *op );
+ virtual void operationRename( Q3NetworkOperation *op );
+ virtual void operationGet( Q3NetworkOperation *op );
+ virtual void operationPut( Q3NetworkOperation *op );
+
+private:
+ int calcBlockSize( int totalSize ) const;
+ QDir dir;
+
+};
+
+#endif // QT_NO_NETWORKPROTOCOL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3LOCALFS_H
diff --git a/src/qt3support/network/q3network.cpp b/src/qt3support/network/q3network.cpp
new file mode 100644
index 0000000000..e3cbeb909e
--- /dev/null
+++ b/src/qt3support/network/q3network.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3network.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "q3networkprotocol.h"
+
+// protocols
+#include "q3ftp.h"
+#include "q3http.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \relates Q3UrlOperator
+
+ This function registers the network protocols for FTP and HTTP.
+ You must call this function before you use QUrlOperator for
+ these protocols.
+*/
+void q3InitNetworkProtocols()
+{
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("ftp"), new Q3NetworkProtocolFactory< Q3Ftp > );
+#endif
+#ifndef QT_NO_NETWORKPROTOCOL_HTTP
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("http"), new Q3NetworkProtocolFactory< Q3Http > );
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_NETWORK
diff --git a/src/qt3support/network/q3network.h b/src/qt3support/network/q3network.h
new file mode 100644
index 0000000000..b363b74839
--- /dev/null
+++ b/src/qt3support/network/q3network.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3NETWORK_H
+#define Q3NETWORK_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORK
+
+Q_COMPAT_EXPORT void q3InitNetworkProtocols();
+
+#endif // QT_NO_NETWORK
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3NETWORK_H
diff --git a/src/qt3support/network/q3networkprotocol.cpp b/src/qt3support/network/q3networkprotocol.cpp
new file mode 100644
index 0000000000..d02f4eae03
--- /dev/null
+++ b/src/qt3support/network/q3networkprotocol.cpp
@@ -0,0 +1,1209 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3networkprotocol.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "q3localfs.h"
+#include "q3urloperator.h"
+#include "qtimer.h"
+#include "qmap.h"
+#include "q3ptrqueue.h"
+#include "q3valuelist.h"
+#include "qurlinfo.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3NETWORKPROTOCOL_DEBUG
+#define NETWORK_OP_DELAY 1000
+
+extern Q_COMPAT_EXPORT Q3NetworkProtocolDict *q3networkProtocolRegister;
+
+Q3NetworkProtocolDict *q3networkProtocolRegister = 0;
+
+class Q3NetworkProtocolPrivate
+{
+public:
+ Q3NetworkProtocolPrivate( Q3NetworkProtocol *p )
+ {
+ url = 0;
+ opInProgress = 0;
+ opStartTimer = new QTimer( p );
+ removeTimer = new QTimer( p );
+ operationQueue.setAutoDelete( false );
+ autoDelete = false;
+ removeInterval = 10000;
+ oldOps.setAutoDelete( false );
+ }
+
+ ~Q3NetworkProtocolPrivate()
+ {
+ removeTimer->stop();
+ if ( opInProgress ) {
+ if ( opInProgress == operationQueue.head() )
+ operationQueue.dequeue();
+ opInProgress->free();
+ }
+ while ( operationQueue.head() ) {
+ operationQueue.head()->free();
+ operationQueue.dequeue();
+ }
+ while ( oldOps.first() ) {
+ oldOps.first()->free();
+ oldOps.removeFirst();
+ }
+ delete opStartTimer;
+ }
+
+ Q3UrlOperator *url;
+ Q3PtrQueue< Q3NetworkOperation > operationQueue;
+ Q3NetworkOperation *opInProgress;
+ QTimer *opStartTimer, *removeTimer;
+ int removeInterval;
+ bool autoDelete;
+ Q3PtrList< Q3NetworkOperation > oldOps;
+};
+
+/*!
+ \class Q3NetworkProtocol
+ \brief The Q3NetworkProtocol class provides a common API for network protocols.
+
+ \compat
+
+ This is a base class which should be used for network protocols
+ implementations that can then be used in Qt (e.g. in the file
+ dialog) together with the Q3UrlOperator.
+
+ The easiest way to implement a new network protocol is to
+ reimplement the operation*() methods, e.g. operationGet(), etc.
+ Only the supported operations should be reimplemented. To specify
+ which operations are supported, also reimplement
+ supportedOperations() and return an int that is OR'd together
+ using the supported operations from the \l
+ Q3NetworkProtocol::Operation enum.
+
+ When you implement a network protocol this way, it is important to
+ emit the correct signals. Also, always emit the finished() signal
+ when an operation is done (on success \e and on failure). Qt
+ relies on correctly emitted finished() signals.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::newChildren( const Q3ValueList<QUrlInfo> &i, Q3NetworkOperation *op )
+
+ This signal is emitted after listChildren() was called and new
+ children (files) have been read from the list of files. \a i holds
+ the information about the new children. \a op is the pointer to
+ the operation object which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+
+ When implementing your own network protocol and reading children,
+ you usually don't read one child at once, but rather a list of
+ them. That's why this signal takes a list of QUrlInfo objects. If
+ you prefer to read just one child at a time you can use the
+ convenience signal newChild(), which takes a single QUrlInfo
+ object.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::newChild( const QUrlInfo &i, Q3NetworkOperation *op )
+
+ This signal is emitted if a new child (file) has been read.
+ Q3NetworkProtocol automatically connects it to a slot which creates
+ a list of QUrlInfo objects (with just one QUrlInfo \a i) and emits
+ the newChildren() signal with this list. \a op is the pointer to
+ the operation object which contains all the information about the
+ operation that has finished, including the state, etc.
+
+ This is just a convenience signal useful for implementing your own
+ network protocol. In all other cases connect to the newChildren()
+ signal with its list of QUrlInfo objects.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::finished( Q3NetworkOperation *op )
+
+ This signal is emitted when an operation finishes. This signal is
+ always emitted, for both success and failure. \a op is the pointer
+ to the operation object which contains all the information about
+ the operation, including the state, etc. Check the state and error
+ code of the operation object to determine whether or not the
+ operation was successful.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::start( Q3NetworkOperation *op )
+
+ Some operations (such as listChildren()) emit this signal when
+ they start processing the operation. \a op is the pointer to the
+ operation object which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::createdDirectory( const QUrlInfo &i, Q3NetworkOperation *op )
+
+ This signal is emitted when mkdir() has been successful and the
+ directory has been created. \a i holds the information about the
+ new directory. \a op is the pointer to the operation object which
+ contains all the information about the operation, including the
+ state, etc. Using op->arg( 0 ), you can get the file name of the
+ new directory.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::removed( Q3NetworkOperation *op )
+
+ This signal is emitted when remove() has been succesiisful and the
+ file has been removed. \a op holds the file name of the removed
+ file in the first argument, accessible with op->arg( 0 ). \a op is
+ the pointer to the operation object which contains all the
+ information about the operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::itemChanged( Q3NetworkOperation *op )
+
+ This signal is emitted whenever a file which is a child of this
+ URL has been changed, e.g. by successfully calling rename(). \a op
+ holds the original and the new file names in the first and second
+ arguments, accessible with op->arg( 0 ) and op->arg( 1 )
+ respectively. \a op is the pointer to the operation object which
+ contains all the information about the operation, including the
+ state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::data( const QByteArray &data,
+ Q3NetworkOperation *op )
+
+ This signal is emitted when new \a data has been received after
+ calling get() or put(). \a op holds the name of the file from
+ which data is retrieved or uploaded in its first argument, and the
+ (raw) data in its second argument. You can get them with
+ op->arg( 0 ) and op->rawArg( 1 ). \a op is the pointer to the
+ operation object, which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator (which is used by the network
+ protocol) emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *op )
+
+ This signal is emitted during the transfer of data (using put() or
+ get()). \a bytesDone is how many bytes of \a bytesTotal have been
+ transferred. \a bytesTotal may be -1, which means that the total
+ number of bytes is not known. \a op is the pointer to the
+ operation object which contains all the information about the
+ operation, including the state, etc.
+
+ When a protocol emits this signal, Q3NetworkProtocol is smart
+ enough to let the Q3UrlOperator, which is used by the network
+ protocol, emit its corresponding signal.
+*/
+
+/*!
+ \fn void Q3NetworkProtocol::connectionStateChanged( int state, const QString &data )
+
+ This signal is emitted whenever the state of the connection of the
+ network protocol is changed. \a state describes the new state,
+ which is one of, \c ConHostFound, \c ConConnected or \c ConClosed.
+ \a data is a message text.
+*/
+
+/*!
+ \enum Q3NetworkProtocol::State
+
+ This enum contains the state that a Q3NetworkOperation can have.
+
+ \value StWaiting The operation is in the Q3NetworkProtocol's queue
+ waiting to be prcessed.
+
+ \value StInProgress The operation is being processed.
+
+ \value StDone The operation has been processed successfully.
+
+ \value StFailed The operation has been processed but an error occurred.
+
+ \value StStopped The operation has been processed but has been
+ stopped before it finished, and is waiting to be processed.
+
+*/
+
+/*!
+ \enum Q3NetworkProtocol::Operation
+
+ This enum lists the possible operations that a network protocol
+ can support. supportedOperations() returns an int of these that is
+ OR'd together. Also, the type() of a Q3NetworkOperation is always
+ one of these values.
+
+ \value OpListChildren List the children of a URL, e.g. of a directory.
+ \value OpMkDir Create a directory.
+ \value OpRemove Remove a child (e.g. a file).
+ \value OpRename Rename a child (e.g. a file).
+ \value OpGet Get data from a location.
+ \value OpPut Put data to a location.
+ \omitvalue OpMkdir
+*/
+
+/*!
+ \enum Q3NetworkProtocol::ConnectionState
+
+ When the connection state of a network protocol changes it emits
+ the signal connectionStateChanged(). The first argument is one of
+ the following values:
+
+ \value ConHostFound Host has been found.
+ \value ConConnected Connection to the host has been established.
+ \value ConClosed Connection has been closed.
+*/
+
+/*!
+ \enum Q3NetworkProtocol::Error
+
+ When an operation fails (finishes unsuccessfully), the
+ Q3NetworkOperation of the operation returns an error code which has
+ one of the following values:
+
+ \value NoError No error occurred.
+
+ \value ErrValid The URL you are operating on is not valid.
+
+ \value ErrUnknownProtocol There is no protocol implementation
+ available for the protocol of the URL you are operating on (e.g.
+ if the protocol is http and no http implementation has been
+ registered).
+
+ \value ErrUnsupported The operation is not supported by the
+ protocol.
+
+ \value ErrParse The URL could not be parsed correctly.
+
+ \value ErrLoginIncorrect You needed to login but the username
+ or password is wrong.
+
+ \value ErrHostNotFound The specified host (in the URL) couldn't
+ be found.
+
+ \value ErrListChildren An error occurred while listing the
+ children (files).
+
+ \value ErrMkDir An error occurred when creating a directory.
+
+ \value ErrRemove An error occurred when removing a child (file).
+
+ \value ErrRename An error occurred when renaming a child (file).
+
+ \value ErrGet An error occurred while getting (retrieving) data.
+
+ \value ErrPut An error occurred while putting (uploading) data.
+
+ \value ErrFileNotExisting A file which is needed by the operation
+ doesn't exist.
+
+ \value ErrPermissionDenied Permission for doing the operation has
+ been denied.
+ \omitvalue ErrMkdir
+ \omitvalue ErrListChlidren
+
+ You should also use these error codes when implementing custom
+ network protocols. If this is not possible, you can define your own
+ error codes by using integer values that don't conflict with any
+ of these values.
+*/
+
+/*!
+ Constructor of the network protocol base class. Does some
+ initialization and connecting of signals and slots.
+*/
+
+Q3NetworkProtocol::Q3NetworkProtocol()
+ : QObject()
+{
+ d = new Q3NetworkProtocolPrivate( this );
+
+ connect( d->opStartTimer, SIGNAL(timeout()),
+ this, SLOT(startOps()) );
+ connect( d->removeTimer, SIGNAL(timeout()),
+ this, SLOT(removeMe()) );
+
+ if ( url() ) {
+ connect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ url(), SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(start(Q3NetworkOperation*)),
+ url(), SIGNAL(start(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+ connect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(removed(Q3NetworkOperation*)),
+ url(), SIGNAL(removed(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(connectionStateChanged(int,QString)),
+ url(), SIGNAL(connectionStateChanged(int,QString)) );
+ }
+
+ connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(processNextOperation(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChild(QUrlInfo,Q3NetworkOperation*)),
+ this, SLOT(emitNewChildren(QUrlInfo,Q3NetworkOperation*)) );
+
+}
+
+/*!
+ Destructor.
+*/
+
+Q3NetworkProtocol::~Q3NetworkProtocol()
+{
+ delete d;
+}
+
+/*!
+ Sets the Q3UrlOperator, on which the protocol works, to \a u.
+
+ \sa Q3UrlOperator
+*/
+
+void Q3NetworkProtocol::setUrl( Q3UrlOperator *u )
+{
+ if ( url() ) {
+ disconnect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ url(), SIGNAL(finished(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(start(Q3NetworkOperation*)),
+ url(), SIGNAL(start(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+ disconnect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(removed(Q3NetworkOperation*)),
+ url(), SIGNAL(removed(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ disconnect( this, SIGNAL(connectionStateChanged(int,QString)),
+ url(), SIGNAL(connectionStateChanged(int,QString)) );
+ }
+
+
+ // ### if autoDelete is true, we should delete the Q3UrlOperator (something
+ // like below; but that is not possible since it would delete this, too).
+ //if ( d->autoDelete && (d->url!=u) ) {
+ // delete d->url; // destructor deletes the network protocol
+ //}
+ d->url = u;
+
+ if ( url() ) {
+ connect( this, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ url(), SIGNAL(data(QByteArray,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(finished(Q3NetworkOperation*)),
+ url(), SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(start(Q3NetworkOperation*)),
+ url(), SIGNAL(start(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(newChildren(Q3ValueList<QUrlInfo>,Q3NetworkOperation*)),
+ url(), SLOT(addEntry(Q3ValueList<QUrlInfo>)) );
+ connect( this, SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)),
+ url(), SIGNAL(createdDirectory(QUrlInfo,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(removed(Q3NetworkOperation*)),
+ url(), SIGNAL(removed(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ url(), SIGNAL(itemChanged(Q3NetworkOperation*)) );
+ connect( this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ url(), SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( this, SIGNAL(connectionStateChanged(int,QString)),
+ url(), SIGNAL(connectionStateChanged(int,QString)) );
+ }
+
+ if ( !d->opInProgress && !d->operationQueue.isEmpty() )
+ d->opStartTimer->start( 0, true );
+}
+
+/*!
+ For processing operations the network protocol base class calls
+ this method quite often. This should be reimplemented by new
+ network protocols. It should return true if the connection is OK
+ (open); otherwise it should return false. If the connection is not
+ open the protocol should open it.
+
+ If the connection can't be opened (e.g. because you already tried
+ but the host couldn't be found), set the state of \a op to
+ Q3NetworkProtocol::StFailed and emit the finished() signal with
+ this Q3NetworkOperation as argument.
+
+ \a op is the operation that needs an open connection.
+*/
+
+bool Q3NetworkProtocol::checkConnection( Q3NetworkOperation * )
+{
+ return true;
+}
+
+/*!
+ Returns an int that is OR'd together using the enum values of
+ \l{Q3NetworkProtocol::Operation}, which describes which operations
+ are supported by the network protocol. Should be reimplemented by
+ new network protocols.
+*/
+
+int Q3NetworkProtocol::supportedOperations() const
+{
+ return 0;
+}
+
+/*!
+ Adds the operation \a op to the operation queue. The operation
+ will be processed as soon as possible. This method returns
+ immediately.
+*/
+
+void Q3NetworkProtocol::addOperation( Q3NetworkOperation *op )
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: addOperation: %p %d", op, op->operation() );
+#endif
+ d->operationQueue.enqueue( op );
+ if ( !d->opInProgress )
+ d->opStartTimer->start( 0, true );
+}
+
+/*!
+ Static method to register a network protocol for Qt. For example,
+ if you have an implementation of NNTP (called Nntp) which is
+ derived from Q3NetworkProtocol, call:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3networkprotocol.cpp 0
+ after which your implementation is registered for future nntp
+ operations.
+
+ The name of the protocol is given in \a protocol and a pointer to
+ the protocol factory is given in \a protocolFactory.
+*/
+
+void Q3NetworkProtocol::registerNetworkProtocol( const QString &protocol,
+ Q3NetworkProtocolFactoryBase *protocolFactory )
+{
+ if ( !q3networkProtocolRegister ) {
+ q3networkProtocolRegister = new Q3NetworkProtocolDict;
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("file"), new Q3NetworkProtocolFactory< Q3LocalFs > );
+ }
+
+ q3networkProtocolRegister->insert( protocol, protocolFactory );
+}
+
+/*!
+ Static method to get a new instance of the network protocol \a
+ protocol. For example, if you need to do some FTP operations, do
+ the following:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3networkprotocol.cpp 1
+ This returns a pointer to a new instance of an ftp implementation
+ or null if no protocol for ftp was registered. The ownership of
+ the pointer is transferred to you, so you must delete it if you
+ don't need it anymore.
+
+ Normally you should not work directly with network protocols, so
+ you will not need to call this method yourself. Instead, use
+ Q3UrlOperator, which makes working with network protocols much more
+ convenient.
+
+ \sa Q3UrlOperator
+*/
+
+Q3NetworkProtocol *Q3NetworkProtocol::getNetworkProtocol( const QString &protocol )
+{
+ if ( !q3networkProtocolRegister ) {
+ q3networkProtocolRegister = new Q3NetworkProtocolDict;
+ Q3NetworkProtocol::registerNetworkProtocol( QLatin1String("file"), new Q3NetworkProtocolFactory< Q3LocalFs > );
+ }
+
+ if ( protocol.isNull() )
+ return 0;
+
+ Q3NetworkProtocolFactoryBase *factory = q3networkProtocolRegister->find( protocol );
+ if ( factory )
+ return factory->createObject();
+
+ return 0;
+}
+
+/*!
+ Returns true if the only protocol registered is for working on the
+ local filesystem; returns false if other network protocols are
+ also registered.
+*/
+
+bool Q3NetworkProtocol::hasOnlyLocalFileSystem()
+{
+ if ( !q3networkProtocolRegister )
+ return false;
+
+ Q3DictIterator< Q3NetworkProtocolFactoryBase > it( *q3networkProtocolRegister );
+ for ( ; it.current(); ++it )
+ if ( it.currentKey() != QLatin1String("file") )
+ return false;
+ return true;
+}
+
+/*!
+ \internal
+ Starts processing network operations.
+*/
+
+void Q3NetworkProtocol::startOps()
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: start processing operations" );
+#endif
+ processNextOperation( 0 );
+}
+
+/*!
+ \internal
+ Processes the operation \a op. It calls the
+ corresponding operation[something]( Q3NetworkOperation * )
+ methods.
+*/
+
+void Q3NetworkProtocol::processOperation( Q3NetworkOperation *op )
+{
+ if ( !op )
+ return;
+
+ switch ( op->operation() ) {
+ case OpListChildren:
+ operationListChildren( op );
+ break;
+ case OpMkDir:
+ operationMkDir( op );
+ break;
+ case OpRemove:
+ operationRemove( op );
+ break;
+ case OpRename:
+ operationRename( op );
+ break;
+ case OpGet:
+ operationGet( op );
+ break;
+ case OpPut:
+ operationPut( op );
+ break;
+ }
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports listing children (files);
+ this method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationListChildren( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports making directories; this
+ method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationMkDir( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports removing children (files);
+ this method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationRemove( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports renaming children (files);
+ this method should then process this Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationRename( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports getting data; this method
+ should then process the Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationGet( Q3NetworkOperation * )
+{
+}
+
+/*!
+ When implementing a new network protocol, this method should be
+ reimplemented if the protocol supports putting (uploading) data;
+ this method should then process the Q3NetworkOperation.
+
+ \a op is the pointer to the operation object which contains all
+ the information on the operation that has finished, including the
+ state, etc.
+*/
+
+void Q3NetworkProtocol::operationPut( Q3NetworkOperation * )
+{
+}
+
+/*! \internal
+*/
+
+void Q3NetworkProtocol::operationPutChunk( Q3NetworkOperation * )
+{
+}
+
+/*!
+ \internal
+ Handles operations. Deletes the previous operation object and
+ tries to process the next operation. It also checks the connection state
+ and only processes the next operation, if the connection of the protocol
+ is open. Otherwise it waits until the protocol opens the connection.
+*/
+
+void Q3NetworkProtocol::processNextOperation( Q3NetworkOperation *old )
+{
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: process next operation, old: %p", old );
+#endif
+ d->removeTimer->stop();
+
+ if ( old )
+ d->oldOps.append( old );
+ if ( d->opInProgress && d->opInProgress!=old )
+ d->oldOps.append( d->opInProgress );
+
+ if ( d->operationQueue.isEmpty() ) {
+ d->opInProgress = 0;
+ if ( d->autoDelete )
+ d->removeTimer->start( d->removeInterval, true );
+ return;
+ }
+
+ Q3NetworkOperation *op = d->operationQueue.head();
+
+ d->opInProgress = op;
+
+ if ( !checkConnection( op ) ) {
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ d->opStartTimer->start( 0, true );
+ } else {
+ d->operationQueue.dequeue();
+ clearOperationQueue();
+ emit finished( op );
+ }
+
+ return;
+ }
+
+ d->opInProgress = op;
+ d->operationQueue.dequeue();
+ processOperation( op );
+}
+
+/*!
+ Returns the Q3UrlOperator on which the protocol works.
+*/
+
+Q3UrlOperator *Q3NetworkProtocol::url() const
+{
+ return d->url;
+}
+
+/*!
+ Returns the operation, which is being processed, or 0 of no
+ operation is being processed at the moment.
+*/
+
+Q3NetworkOperation *Q3NetworkProtocol::operationInProgress() const
+{
+ return d->opInProgress;
+}
+
+/*!
+ Clears the operation queue.
+*/
+
+void Q3NetworkProtocol::clearOperationQueue()
+{
+ d->operationQueue.dequeue();
+ d->operationQueue.setAutoDelete( true );
+ d->operationQueue.clear();
+}
+
+/*!
+ Stops the current operation that is being processed and clears all
+ waiting operations.
+*/
+
+void Q3NetworkProtocol::stop()
+{
+ Q3NetworkOperation *op = d->opInProgress;
+ clearOperationQueue();
+ if ( op ) {
+ op->setState( StStopped );
+ op->setProtocolDetail( tr( "Operation stopped by the user" ) );
+ emit finished( op );
+ setUrl( 0 );
+ op->free();
+ }
+}
+
+/*!
+ Because it's sometimes hard to take care of removing network
+ protocol instances, Q3NetworkProtocol provides an auto-delete
+ mechanism. If you set \a b to true, the network protocol instance
+ is removed after it has been inactive for \a i milliseconds (i.e.
+ \a i milliseconds after the last operation has been processed).
+ If you set \a b to false the auto-delete mechanism is switched
+ off.
+
+ If you switch on auto-delete, the Q3NetworkProtocol also deletes
+ its Q3UrlOperator.
+*/
+
+void Q3NetworkProtocol::setAutoDelete( bool b, int i )
+{
+ d->autoDelete = b;
+ d->removeInterval = i;
+}
+
+/*!
+ Returns true if auto-deleting is enabled; otherwise returns false.
+
+ \sa Q3NetworkProtocol::setAutoDelete()
+*/
+
+bool Q3NetworkProtocol::autoDelete() const
+{
+ return d->autoDelete;
+}
+
+/*!
+ \internal
+*/
+
+void Q3NetworkProtocol::removeMe()
+{
+ if ( d->autoDelete ) {
+#ifdef Q3NETWORKPROTOCOL_DEBUG
+ qDebug( "Q3NetworkOperation: autodelete of Q3NetworkProtocol %p", this );
+#endif
+ delete d->url; // destructor deletes the network protocol
+ }
+}
+
+void Q3NetworkProtocol::emitNewChildren( const QUrlInfo &i, Q3NetworkOperation *op )
+{
+ Q3ValueList<QUrlInfo> lst;
+ lst << i;
+ emit newChildren( lst, op );
+}
+
+class Q3NetworkOperationPrivate
+{
+public:
+ Q3NetworkProtocol::Operation operation;
+ Q3NetworkProtocol::State state;
+ QMap<int, QString> args;
+ QMap<int, QByteArray> rawArgs;
+ QString protocolDetail;
+ int errorCode;
+ QTimer *deleteTimer;
+};
+
+/*!
+ \class Q3NetworkOperation
+
+ \brief The Q3NetworkOperation class provides common operations for network protocols.
+
+ \compat
+
+ An object is created to describe the operation and the current
+ state for each operation that a network protocol should process.
+
+ \sa Q3NetworkProtocol
+*/
+
+/*!
+ Constructs a network operation object. \a operation is the type of
+ the operation, and \a arg0, \a arg1 and \a arg2 are the first
+ three arguments of the operation. The state is initialized to
+ Q3NetworkProtocol::StWaiting.
+
+ \sa Q3NetworkProtocol::Operation Q3NetworkProtocol::State
+*/
+
+Q3NetworkOperation::Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QString &arg0, const QString &arg1,
+ const QString &arg2 )
+{
+ d = new Q3NetworkOperationPrivate;
+ d->deleteTimer = new QTimer( this );
+ connect( d->deleteTimer, SIGNAL(timeout()),
+ this, SLOT(deleteMe()) );
+ d->operation = operation;
+ d->state = Q3NetworkProtocol::StWaiting;
+ d->args[ 0 ] = arg0;
+ d->args[ 1 ] = arg1;
+ d->args[ 2 ] = arg2;
+ d->rawArgs[ 0 ] = QByteArray( 0 );
+ d->rawArgs[ 1 ] = QByteArray( 0 );
+ d->rawArgs[ 2 ] = QByteArray( 0 );
+ d->protocolDetail.clear();
+ d->errorCode = (int)Q3NetworkProtocol::NoError;
+}
+
+/*!
+ Constructs a network operation object. \a operation is the type of
+ the operation, and \a arg0, \a arg1 and \a arg2 are the first
+ three raw data arguments of the operation. The state is
+ initialized to Q3NetworkProtocol::StWaiting.
+
+ \sa Q3NetworkProtocol::Operation Q3NetworkProtocol::State
+*/
+
+Q3NetworkOperation::Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QByteArray &arg0, const QByteArray &arg1,
+ const QByteArray &arg2 )
+{
+ d = new Q3NetworkOperationPrivate;
+ d->deleteTimer = new QTimer( this );
+ connect( d->deleteTimer, SIGNAL(timeout()),
+ this, SLOT(deleteMe()) );
+ d->operation = operation;
+ d->state = Q3NetworkProtocol::StWaiting;
+ d->args[ 0 ].clear();
+ d->args[ 1 ].clear();
+ d->args[ 2 ].clear();
+ d->rawArgs[ 0 ] = arg0;
+ d->rawArgs[ 1 ] = arg1;
+ d->rawArgs[ 2 ] = arg2;
+ d->protocolDetail.clear();
+ d->errorCode = (int)Q3NetworkProtocol::NoError;
+}
+
+/*!
+ Destructor.
+*/
+
+Q3NetworkOperation::~Q3NetworkOperation()
+{
+ qDeleteInEventHandler(d->deleteTimer);
+ delete d;
+}
+
+/*!
+ Sets the \a state of the operation object. This should be done by
+ the network protocol during processing; at the end it should be
+ set to Q3NetworkProtocol::StDone or Q3NetworkProtocol::StFailed,
+ depending on success or failure.
+
+ \sa Q3NetworkProtocol::State
+*/
+
+void Q3NetworkOperation::setState( Q3NetworkProtocol::State state )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->state = state;
+}
+
+/*!
+ If the operation failed, the error message can be specified as \a
+ detail.
+*/
+
+void Q3NetworkOperation::setProtocolDetail( const QString &detail )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->protocolDetail = detail;
+}
+
+/*!
+ Sets the error code to \a ec.
+
+ If the operation failed, the protocol should set an error code to
+ describe the error in more detail. If possible, one of the error
+ codes defined in Q3NetworkProtocol should be used.
+
+ \sa setProtocolDetail() Q3NetworkProtocol::Error
+*/
+
+void Q3NetworkOperation::setErrorCode( int ec )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->errorCode = ec;
+}
+
+/*!
+ Sets the network operation's \a{num}-th argument to \a arg.
+*/
+
+void Q3NetworkOperation::setArg( int num, const QString &arg )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->args[ num ] = arg;
+}
+
+/*!
+ Sets the network operation's \a{num}-th raw data argument to \a arg.
+*/
+
+void Q3NetworkOperation::setRawArg( int num, const QByteArray &arg )
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ d->rawArgs[ num ] = arg;
+}
+
+/*!
+ Returns the type of the operation.
+*/
+
+Q3NetworkProtocol::Operation Q3NetworkOperation::operation() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->operation;
+}
+
+/*!
+ Returns the state of the operation. You can determine whether an
+ operation is still waiting to be processed, is being processed,
+ has been processed successfully, or failed.
+*/
+
+Q3NetworkProtocol::State Q3NetworkOperation::state() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->state;
+}
+
+/*!
+ Returns the operation's \a{num}-th argument. If this argument was
+ not already set, an empty string is returned.
+*/
+
+QString Q3NetworkOperation::arg( int num ) const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->args[ num ];
+}
+
+/*!
+ Returns the operation's \a{num}-th raw data argument. If this
+ argument was not already set, an empty bytearray is returned.
+*/
+
+QByteArray Q3NetworkOperation::rawArg( int num ) const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->rawArgs[ num ];
+}
+
+/*!
+ Returns a detailed error message for the last error. This must
+ have been set using setProtocolDetail().
+*/
+
+QString Q3NetworkOperation::protocolDetail() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->protocolDetail;
+}
+
+/*!
+ Returns the error code for the last error that occurred.
+*/
+
+int Q3NetworkOperation::errorCode() const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->errorCode;
+}
+
+/*!
+ \internal
+*/
+
+QByteArray& Q3NetworkOperation::raw( int num ) const
+{
+ if ( d->deleteTimer->isActive() ) {
+ d->deleteTimer->stop();
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+ }
+ return d->rawArgs[ num ];
+}
+
+/*!
+ Sets this object to delete itself when it hasn't been used for one
+ second.
+
+ Because Q3NetworkOperation pointers are passed around a lot the
+ Q3NetworkProtocol generally does not have enough knowledge to
+ delete these at the correct time. If a Q3NetworkProtocol doesn't
+ need an operation any more it will call this function instead.
+
+ Note: you should never need to call the method yourself.
+*/
+
+void Q3NetworkOperation::free()
+{
+ d->deleteTimer->start( NETWORK_OP_DELAY );
+}
+
+/*!
+ \internal
+ Internal slot for auto-deletion.
+*/
+
+void Q3NetworkOperation::deleteMe()
+{
+ delete this;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_q3networkprotocol.cpp"
+
+#endif
diff --git a/src/qt3support/network/q3networkprotocol.h b/src/qt3support/network/q3networkprotocol.h
new file mode 100644
index 0000000000..efe2ecf3ab
--- /dev/null
+++ b/src/qt3support/network/q3networkprotocol.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3NETWORKPROTOCOL_H
+#define Q3NETWORKPROTOCOL_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qobject.h>
+#include <Qt3Support/q3dict.h>
+#include <Qt3Support/q3valuelist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+class Q3NetworkProtocol;
+class Q3NetworkOperation;
+class QTimer;
+class Q3UrlOperator;
+class Q3NetworkProtocolPrivate;
+class QUrlInfo;
+template <class T> class Q3ValueList;
+
+class Q_COMPAT_EXPORT Q3NetworkProtocolFactoryBase
+{
+public:
+ virtual ~Q3NetworkProtocolFactoryBase() {}
+ virtual Q3NetworkProtocol *createObject() = 0;
+};
+
+template< class T >
+class Q3NetworkProtocolFactory : public Q3NetworkProtocolFactoryBase
+{
+public:
+ Q3NetworkProtocol *createObject() {
+ return new T;
+ }
+
+};
+
+typedef Q3Dict< Q3NetworkProtocolFactoryBase > Q3NetworkProtocolDict;
+
+class Q_COMPAT_EXPORT Q3NetworkProtocol : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum State {
+ StWaiting = 0,
+ StInProgress,
+ StDone,
+ StFailed,
+ StStopped
+ };
+
+ enum Operation {
+ OpListChildren = 1,
+ OpMkDir = 2,
+ OpMkdir = OpMkDir, // ### remove in 4.0
+ OpRemove = 4,
+ OpRename = 8,
+ OpGet = 32,
+ OpPut = 64
+ };
+
+ enum ConnectionState {
+ ConHostFound,
+ ConConnected,
+ ConClosed
+ };
+
+ enum Error {
+ // no error
+ NoError = 0,
+ // general errors
+ ErrValid,
+ ErrUnknownProtocol,
+ ErrUnsupported,
+ ErrParse,
+ // errors on connect
+ ErrLoginIncorrect,
+ ErrHostNotFound,
+ // protocol errors
+ ErrListChildren,
+ ErrListChlidren = ErrListChildren, // ### remove in 4.0
+ ErrMkDir,
+ ErrMkdir = ErrMkDir, // ### remove in 4.0
+ ErrRemove,
+ ErrRename,
+ ErrGet,
+ ErrPut,
+ ErrFileNotExisting,
+ ErrPermissionDenied
+ };
+
+ Q3NetworkProtocol();
+ virtual ~Q3NetworkProtocol();
+
+ virtual void setUrl( Q3UrlOperator *u );
+
+ virtual void setAutoDelete( bool b, int i = 10000 );
+ bool autoDelete() const;
+
+ static void registerNetworkProtocol( const QString &protocol,
+ Q3NetworkProtocolFactoryBase *protocolFactory );
+ static Q3NetworkProtocol *getNetworkProtocol( const QString &protocol );
+ static bool hasOnlyLocalFileSystem();
+
+ virtual int supportedOperations() const;
+ virtual void addOperation( Q3NetworkOperation *op );
+
+ Q3UrlOperator *url() const;
+ Q3NetworkOperation *operationInProgress() const;
+ virtual void clearOperationQueue();
+ virtual void stop();
+
+Q_SIGNALS:
+ void data( const QByteArray &, Q3NetworkOperation *res );
+ void connectionStateChanged( int state, const QString &data );
+ void finished( Q3NetworkOperation *res );
+ void start( Q3NetworkOperation *res );
+ void newChildren( const Q3ValueList<QUrlInfo> &, Q3NetworkOperation *res );
+ void newChild( const QUrlInfo &, Q3NetworkOperation *res );
+ void createdDirectory( const QUrlInfo &, Q3NetworkOperation *res );
+ void removed( Q3NetworkOperation *res );
+ void itemChanged( Q3NetworkOperation *res );
+ void dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *res );
+
+protected:
+ virtual void processOperation( Q3NetworkOperation *op );
+ virtual void operationListChildren( Q3NetworkOperation *op );
+ virtual void operationMkDir( Q3NetworkOperation *op );
+ virtual void operationRemove( Q3NetworkOperation *op );
+ virtual void operationRename( Q3NetworkOperation *op );
+ virtual void operationGet( Q3NetworkOperation *op );
+ virtual void operationPut( Q3NetworkOperation *op );
+ virtual void operationPutChunk( Q3NetworkOperation *op );
+ virtual bool checkConnection( Q3NetworkOperation *op );
+
+private:
+ Q3NetworkProtocolPrivate *d;
+
+private Q_SLOTS:
+ void processNextOperation( Q3NetworkOperation *old );
+ void startOps();
+ void emitNewChildren( const QUrlInfo &i, Q3NetworkOperation *op );
+
+ void removeMe();
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3NetworkProtocol( const Q3NetworkProtocol & );
+ Q3NetworkProtocol &operator=( const Q3NetworkProtocol & );
+#endif
+};
+
+class Q3NetworkOperationPrivate;
+
+class Q_COMPAT_EXPORT Q3NetworkOperation : public QObject
+{
+ Q_OBJECT
+ friend class Q3UrlOperator;
+
+public:
+ Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QString &arg0, const QString &arg1,
+ const QString &arg2 );
+ Q3NetworkOperation( Q3NetworkProtocol::Operation operation,
+ const QByteArray &arg0, const QByteArray &arg1,
+ const QByteArray &arg2 );
+ ~Q3NetworkOperation();
+
+ void setState( Q3NetworkProtocol::State state );
+ void setProtocolDetail( const QString &detail );
+ void setErrorCode( int ec );
+ void setArg( int num, const QString &arg );
+ void setRawArg( int num, const QByteArray &arg );
+
+ Q3NetworkProtocol::Operation operation() const;
+ Q3NetworkProtocol::State state() const;
+ QString arg( int num ) const;
+ QByteArray rawArg( int num ) const;
+ QString protocolDetail() const;
+ int errorCode() const;
+
+ void free();
+
+private Q_SLOTS:
+ void deleteMe();
+
+private:
+ QByteArray &raw( int num ) const;
+ Q3NetworkOperationPrivate *d;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3NetworkOperation( const Q3NetworkOperation & );
+ Q3NetworkOperation &operator=( const Q3NetworkOperation & );
+#endif
+};
+
+#endif // QT_NO_NETWORKPROTOCOL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3NETWORKPROTOCOL_H
diff --git a/src/qt3support/network/q3serversocket.cpp b/src/qt3support/network/q3serversocket.cpp
new file mode 100644
index 0000000000..f61b65219d
--- /dev/null
+++ b/src/qt3support/network/q3serversocket.cpp
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3serversocket.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qsocketnotifier.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3ServerSocketPrivate {
+public:
+ Q3ServerSocketPrivate(): s(0), n(0) {}
+ ~Q3ServerSocketPrivate() { delete n; delete s; }
+ Q3SocketDevice *s;
+ QSocketNotifier *n;
+};
+
+
+/*!
+ \class Q3ServerSocket
+ \brief The Q3ServerSocket class provides a TCP-based server.
+
+ \compat
+
+ This class is a convenience class for accepting incoming TCP
+ connections. You can specify the port or have Q3ServerSocket pick
+ one, and listen on just one address or on all the machine's
+ addresses.
+
+ Using the API is very simple: subclass Q3ServerSocket, call the
+ constructor of your choice, and implement newConnection() to
+ handle new incoming connections. There is nothing more to do.
+
+ (Note that due to lack of support in the underlying APIs,
+ Q3ServerSocket cannot accept or reject connections conditionally.)
+
+ \sa Q3Socket, Q3SocketDevice, QHostAddress, QSocketNotifier
+*/
+
+
+/*!
+ Creates a server socket object, that will serve the given \a port
+ on all the addresses of this host. If \a port is 0, Q3ServerSocket
+ will pick a suitable port in a system-dependent manner. Use \a
+ backlog to specify how many pending connections the server can
+ have.
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+
+ \warning On Tru64 Unix systems a value of 0 for \a backlog means
+ that you don't accept any connections at all; you should specify a
+ value larger than 0.
+*/
+
+Q3ServerSocket::Q3ServerSocket( Q_UINT16 port, int backlog,
+ QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new Q3ServerSocketPrivate;
+ init( QHostAddress(), port, backlog );
+}
+
+
+/*!
+ Creates a server socket object, that will serve the given \a port
+ only on the given \a address. Use \a backlog to specify how many
+ pending connections the server can have.
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+
+ \warning On Tru64 Unix systems a value of 0 for \a backlog means
+ that you don't accept any connections at all; you should specify a
+ value larger than 0.
+*/
+
+Q3ServerSocket::Q3ServerSocket( const QHostAddress & address, Q_UINT16 port,
+ int backlog,
+ QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new Q3ServerSocketPrivate;
+ init( address, port, backlog );
+}
+
+
+/*!
+ Construct an empty server socket.
+
+ This constructor, in combination with setSocket(), allows us to
+ use the Q3ServerSocket class as a wrapper for other socket types
+ (e.g. Unix Domain Sockets under Unix).
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+
+ \sa setSocket()
+*/
+
+Q3ServerSocket::Q3ServerSocket( QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new Q3ServerSocketPrivate;
+}
+
+
+/*!
+ Returns true if the construction succeeded; otherwise returns false.
+*/
+bool Q3ServerSocket::ok() const
+{
+ return !!d->s;
+}
+
+/*
+ The common bit of the constructors.
+ */
+void Q3ServerSocket::init( const QHostAddress & address, Q_UINT16 port, int backlog )
+{
+ d->s = new Q3SocketDevice( Q3SocketDevice::Stream, address.isIPv4Address()
+ ? Q3SocketDevice::IPv4 : Q3SocketDevice::IPv6, 0 );
+#if !defined(Q_OS_WIN32)
+ // Under Unix, we want to be able to use the port, even if a socket on the
+ // same address-port is in TIME_WAIT. Under Windows this is possible anyway
+ // -- furthermore, the meaning of reusable is different: it means that you
+ // can use the same address-port for multiple listening sockets.
+ d->s->setAddressReusable( true );
+#endif
+ if ( d->s->bind( address, port )
+ && d->s->listen( backlog ) )
+ {
+ d->n = new QSocketNotifier( d->s->socket(), QSocketNotifier::Read,
+ this, "accepting new connections" );
+ connect( d->n, SIGNAL(activated(int)),
+ this, SLOT(incomingConnection(int)) );
+ } else {
+ qWarning( "Q3ServerSocket: failed to bind or listen to the socket" );
+ delete d->s;
+ d->s = 0;
+ }
+}
+
+
+/*!
+ Destroys the socket.
+
+ This causes any backlogged connections (connections that have
+ reached the host, but not yet been completely set up by calling
+ Q3SocketDevice::accept()) to be severed.
+
+ Existing connections continue to exist; this only affects the
+ acceptance of new connections.
+*/
+Q3ServerSocket::~Q3ServerSocket()
+{
+ delete d;
+}
+
+
+/*!
+ \fn void Q3ServerSocket::newConnection( int socket )
+
+ This pure virtual function is responsible for setting up a new
+ incoming connection. \a socket is the fd (file descriptor) for the
+ newly accepted connection.
+*/
+
+
+void Q3ServerSocket::incomingConnection( int )
+{
+ int fd = d->s->accept();
+ if ( fd >= 0 )
+ newConnection( fd );
+}
+
+
+/*!
+ Returns the port number on which this server socket listens. This
+ is always non-zero; if you specify 0 in the constructor,
+ Q3ServerSocket will pick a non-zero port itself. ok() must be true
+ before calling this function.
+
+ \sa address() Q3SocketDevice::port()
+*/
+Q_UINT16 Q3ServerSocket::port() const
+{
+ if ( !d || !d->s )
+ return 0;
+ return d->s->port();
+}
+
+
+/*!
+ Returns the operating system socket.
+*/
+int Q3ServerSocket::socket() const
+{
+ if ( !d || !d->s )
+ return -1;
+
+ return d->s->socket();
+}
+
+/*!
+ Returns the address on which this object listens, or 0.0.0.0 if
+ this object listens on more than one address. ok() must be true
+ before calling this function.
+
+ \sa port() Q3SocketDevice::address()
+*/
+QHostAddress Q3ServerSocket::address() const
+{
+ if ( !d || !d->s )
+ return QHostAddress();
+
+ return d->s->address();
+}
+
+
+/*!
+ Returns a pointer to the internal socket device. The returned
+ pointer is 0 if there is no connection or pending connection.
+
+ There is normally no need to manipulate the socket device directly
+ since this class does all the necessary setup for most client or
+ server socket applications.
+*/
+Q3SocketDevice *Q3ServerSocket::socketDevice()
+{
+ if ( !d )
+ return 0;
+
+ return d->s;
+}
+
+
+/*!
+ Sets the socket to use \a socket. bind() and listen() should
+ already have been called for \a socket.
+
+ This allows us to use the Q3ServerSocket class as a wrapper for
+ other socket types (e.g. Unix Domain Sockets).
+*/
+void Q3ServerSocket::setSocket( int socket )
+{
+ delete d;
+ d = new Q3ServerSocketPrivate;
+ d->s = new Q3SocketDevice( socket, Q3SocketDevice::Stream );
+ d->n = new QSocketNotifier( d->s->socket(), QSocketNotifier::Read,
+ this, "accepting new connections" );
+ connect( d->n, SIGNAL(activated(int)),
+ this, SLOT(incomingConnection(int)) );
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3serversocket.h b/src/qt3support/network/q3serversocket.h
new file mode 100644
index 0000000000..e26e1a5c23
--- /dev/null
+++ b/src/qt3support/network/q3serversocket.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SERVERSOCKET_H
+#define Q3SERVERSOCKET_H
+
+#include <QtCore/qobject.h>
+#include <QtNetwork/qhostaddress.h>
+#include <Qt3Support/q3socketdevice.h> // ### remove or keep for users' convenience?
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+class Q3ServerSocketPrivate;
+
+class Q_COMPAT_EXPORT Q3ServerSocket : public QObject
+{
+ Q_OBJECT
+public:
+ Q3ServerSocket( Q_UINT16 port, int backlog = 1,
+ QObject *parent=0, const char *name=0 );
+ Q3ServerSocket( const QHostAddress & address, Q_UINT16 port, int backlog = 1,
+ QObject *parent=0, const char *name=0 );
+ Q3ServerSocket( QObject *parent=0, const char *name=0 );
+ virtual ~Q3ServerSocket();
+
+ bool ok() const;
+
+ Q_UINT16 port() const ;
+
+ int socket() const ;
+ virtual void setSocket( int socket );
+
+ QHostAddress address() const ;
+
+ virtual void newConnection( int socket ) = 0;
+
+protected:
+ Q3SocketDevice *socketDevice();
+
+private Q_SLOTS:
+ void incomingConnection( int socket );
+
+private:
+ Q3ServerSocketPrivate *d;
+ void init( const QHostAddress & address, Q_UINT16 port, int backlog );
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SERVERSOCKET_H
diff --git a/src/qt3support/network/q3socket.cpp b/src/qt3support/network/q3socket.cpp
new file mode 100644
index 0000000000..01585f0a6e
--- /dev/null
+++ b/src/qt3support/network/q3socket.cpp
@@ -0,0 +1,1518 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socket.h"
+#ifndef QT_NO_NETWORK
+#include "q3ptrlist.h"
+#include "qtimer.h"
+#include "q3socketdevice.h"
+#include "q3dns.h"
+#include "private/q3membuf_p.h"
+
+#include <string.h>
+#ifndef NO_ERRNO_H
+#if defined(Q_OS_WINCE)
+#include "qfunctions_wince.h"
+#else
+#include <errno.h>
+#endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3SOCKET_DEBUG
+
+/*
+ Perhaps this private functionality needs to be refactored.
+
+ Comment from Robert D Gatlin (Intel):
+
+ It would be nice to have the functionality inherent in Q3Socket available
+ as a separate class as a standard part of the Qt library, something along
+ the line of:
+
+ class QByteBuffer : public QIODevice { ... }
+
+ The same class could/would be used within Q3Socket for the Read/Write
+ buffers.
+
+ The above class could be used in the following way(s):
+
+ buffer.open( IO_WriteOnly | IO_Append );
+ buffer.writeBlock( a ); // a = QByteArray
+ buffer.close();
+
+ QByteArray b;
+ b.resize( buffer.size() );
+ buffer.open( IO_ReadOnly );
+ buffer.readBlock( b.data(), b.size() );
+ buffer.close();
+
+ But would also be useable with QDataStream (via QIODevice) with:
+
+ buffer.open( IO_WriteOnly | IO_Append );
+ QDataStream is( &buffer );
+ is << 100;
+ buffer.close();
+
+ buffer.open( IO_ReadOnly );
+ QDataStream os( &buffer );
+ Q_UINT32 x;
+ os >> x;
+ buffer.close();
+
+ The real usefulness is with any situations where data (QByteArray) arrives
+ incrementally (as in Q3Socket and filter case above).
+
+ I tried using QBuffer, but QBuffer does not trim bytes from the front of
+ the buffer in cases like:
+
+ QBuffer buf;
+ buf.open( IO_ReadOnly );
+ QDataStream ds( &buf );
+ Q_INT32 x;
+ ds >> x;
+ buf.close();
+
+ In the above case, buf.size() will be identical before and after the
+ operation with QDataStream. Based on the implementation of QBuffer, it
+ does not appear well suited for this kind of operation.
+*/
+
+// Private class for Q3Socket
+
+class Q3SocketPrivate {
+public:
+ Q3SocketPrivate();
+ ~Q3SocketPrivate();
+ void closeSocket();
+ void close();
+ void connectionClosed();
+ void setSocketDevice( Q3Socket *q, Q3SocketDevice *device );
+
+ Q3Socket::State state; // connection state
+ QString host; // host name
+ Q_UINT16 port; // host port
+ Q3SocketDevice *socket; // connection socket
+ QSocketNotifier *rsn, *wsn; // socket notifiers
+ Q3Membuf rba; // read buffer
+ Q_ULONG readBufferSize; // limit for the read buffer size
+ Q3PtrList<QByteArray> wba; // list of write bufs
+ QHostAddress addr; // connection address
+ Q3ValueList<QHostAddress> addresses; // alternatives looked up
+ QIODevice::Offset wsize; // write total buf size
+ QIODevice::Offset windex; // write index
+#ifndef QT_NO_DNS
+ Q3Dns *dns4;
+ Q3Dns *dns6;
+#endif
+ static Q3PtrList<Q3Socket> sn_read_alreadyCalled; // used to avoid unwanted recursion
+ Q3ValueList<QHostAddress> l4;
+ Q3ValueList<QHostAddress> l6;
+};
+
+Q3PtrList<Q3Socket> Q3SocketPrivate::sn_read_alreadyCalled;
+
+Q3SocketPrivate::Q3SocketPrivate()
+ : state(Q3Socket::Idle), host(QString::fromLatin1("")), port(0),
+ socket(0), rsn(0), wsn(0), readBufferSize(0), wsize(0), windex(0)
+{
+#ifndef QT_NO_DNS
+ dns4 = 0;
+ dns6 = 0;
+#endif
+ wba.setAutoDelete( true );
+}
+
+Q3SocketPrivate::~Q3SocketPrivate()
+{
+ close();
+ delete socket;
+#ifndef QT_NO_DNS
+ delete dns4;
+ delete dns6;
+#endif
+}
+
+extern void qDeleteInEventHandler(QObject *o);
+void Q3SocketPrivate::closeSocket()
+{
+ // Order is important here - the socket notifiers must go away
+ // before the socket does, otherwise libc or the kernel will
+ // become unhappy.
+ if (rsn) {
+ qDeleteInEventHandler(rsn);
+ rsn = 0;
+ }
+ if (wsn) {
+ qDeleteInEventHandler(wsn);
+ wsn = 0;
+ }
+ if ( socket )
+ socket->close();
+}
+
+void Q3SocketPrivate::close()
+{
+ closeSocket();
+ wsize = 0;
+ rba.clear(); wba.clear();
+ windex = 0;
+}
+
+void Q3SocketPrivate::connectionClosed()
+{
+ // We keep the open state in case there's unread incoming data
+ state = Q3Socket::Idle;
+ closeSocket();
+ wba.clear();
+ windex = wsize = 0;
+}
+
+void Q3SocketPrivate::setSocketDevice( Q3Socket *q, Q3SocketDevice *device )
+{
+ delete socket;
+ delete rsn;
+ delete wsn;
+
+ if ( device ) {
+ socket = device;
+ } else {
+ socket = new Q3SocketDevice( Q3SocketDevice::Stream,
+ ( addr.isIPv4Address() ?
+ Q3SocketDevice::IPv4 :
+ Q3SocketDevice::IPv6 ), 0 );
+ socket->setBlocking( false );
+ socket->setAddressReusable( true );
+ }
+
+ rsn = new QSocketNotifier( socket->socket(),
+ QSocketNotifier::Read, q, "read" );
+ wsn = new QSocketNotifier( socket->socket(),
+ QSocketNotifier::Write, q, "write" );
+
+ QObject::connect( rsn, SIGNAL(activated(int)), q, SLOT(sn_read()) );
+ rsn->setEnabled( false );
+ QObject::connect( wsn, SIGNAL(activated(int)), q, SLOT(sn_write()) );
+ wsn->setEnabled( false );
+}
+
+/*!
+ \class Q3Socket
+ \brief The Q3Socket class provides a buffered TCP connection.
+
+ \compat
+
+ It provides a totally non-blocking QIODevice, and modifies and
+ extends the API of QIODevice with socket-specific code.
+
+ The functions you're likely to call most are connectToHost(),
+ bytesAvailable(), canReadLine() and the ones it inherits from
+ QIODevice.
+
+ connectToHost() is the most-used function. As its name implies,
+ it opens a connection to a named host.
+
+ Most network protocols are either packet-oriented or
+ line-oriented. canReadLine() indicates whether a connection
+ contains an entire unread line or not, and bytesAvailable()
+ returns the number of bytes available for reading.
+
+ The signals error(), connected(), readyRead() and
+ connectionClosed() inform you of the progress of the connection.
+ There are also some less commonly used signals. hostFound() is
+ emitted when connectToHost() has finished its DNS lookup and is
+ starting its TCP connection. delayedCloseFinished() is emitted
+ when close() succeeds. bytesWritten() is emitted when Q3Socket
+ moves data from its "to be written" queue into the TCP
+ implementation.
+
+ There are several access functions for the socket: state() returns
+ whether the object is idle, is doing a DNS lookup, is connecting,
+ has an operational connection, etc. address() and port() return
+ the IP address and port used for the connection. The peerAddress()
+ and peerPort() functions return the IP address and port used by
+ the peer, and peerName() returns the name of the peer (normally
+ the name that was passed to connectToHost()). socketDevice()
+ returns a pointer to the Q3SocketDevice used for this socket.
+
+ Q3Socket inherits QIODevice, and reimplements some functions. In
+ general, you can treat it as a QIODevice for writing, and mostly
+ also for reading. The match isn't perfect, since the QIODevice
+ API is designed for devices that are controlled by the same
+ machine, and an asynchronous peer-to-peer network connection isn't
+ quite like that. For example, there is nothing that matches
+ QIODevice::size() exactly. The documentation for open(), close(),
+ flush(), size(), at(), atEnd(), readBlock(), writeBlock(),
+ getch(), putch(), ungetch() and readLine() describes the
+ differences in detail.
+
+ \warning Q3Socket is not suitable for use in threads. If you need
+ to uses sockets in threads use the lower-level Q3SocketDevice class.
+
+ \sa Q3SocketDevice, QHostAddress, QSocketNotifier
+*/
+
+
+/*!
+ Creates a Q3Socket object in Q3Socket::Idle state.
+
+ The \a parent and \a name arguments are passed on to the QObject
+ constructor.
+*/
+
+Q3Socket::Q3Socket( QObject *parent, const char *name )
+ : QIODevice( parent )
+{
+ setObjectName(QLatin1String(name));
+ d = new Q3SocketPrivate;
+ setSocketDevice( 0 );
+ resetStatus();
+}
+
+
+/*!
+ Destroys the socket. Closes the connection if necessary.
+
+ \sa close()
+*/
+
+Q3Socket::~Q3Socket()
+{
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): Destroy", name() );
+#endif
+ if ( state() != Idle )
+ close();
+ Q_ASSERT( d != 0 );
+ delete d;
+}
+
+
+/*!
+ Returns a pointer to the internal socket device.
+
+ There is normally no need to manipulate the socket device directly
+ since this class does the necessary setup for most applications.
+*/
+
+Q3SocketDevice *Q3Socket::socketDevice()
+{
+ return d->socket;
+}
+
+/*!
+ Sets the internal socket device to \a device. Passing a \a device
+ of 0 will cause the internal socket device to be used. Any
+ existing connection will be disconnected before using the new \a
+ device.
+
+ The new device should not be connected before being associated
+ with a Q3Socket; after setting the socket call connectToHost() to
+ make the connection.
+
+ This function is useful if you need to subclass Q3SocketDevice and
+ want to use the Q3Socket API, for example, to implement Unix domain
+ sockets.
+*/
+
+void Q3Socket::setSocketDevice( Q3SocketDevice *device )
+{
+ if ( state() != Idle )
+ close();
+ d->setSocketDevice( this, device );
+}
+
+/*!
+ \enum Q3Socket::State
+
+ This enum defines the connection states:
+
+ \value Idle if there is no connection
+ \value HostLookup during a DNS lookup
+ \value Connecting during TCP connection establishment
+ \value Connected when there is an operational connection
+ \value Closing if the socket is closing down, but is not yet closed.
+ \omitvalue Connection
+*/
+
+/*!
+ Returns the current state of the socket connection.
+
+ \sa Q3Socket::State
+*/
+
+Q3Socket::State Q3Socket::state() const
+{
+ return d->state;
+}
+
+
+#ifndef QT_NO_DNS
+
+/*!
+ Attempts to make a connection to \a host on the specified \a port
+ and return immediately.
+
+ Any connection or pending connection is closed immediately, and
+ Q3Socket goes into the \c HostLookup state. When the lookup
+ succeeds, it emits hostFound(), starts a TCP connection and goes
+ into the \c Connecting state. Finally, when the connection
+ succeeds, it emits connected() and goes into the \c Connected
+ state. If there is an error at any point, it emits error().
+
+ \a host may be an IP address in string form, or it may be a DNS
+ name. Q3Socket will do a normal DNS lookup if required. Note that
+ \a port is in native byte order, unlike some other libraries.
+
+ \sa state()
+*/
+
+void Q3Socket::connectToHost( const QString &host, Q_UINT16 port )
+{
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::connectToHost: host %s, port %d",
+ name(), host.ascii(), port );
+#endif
+ setSocketIntern( -1 );
+ d->state = HostLookup;
+ d->host = host;
+ d->port = port;
+ d->dns4 = new Q3Dns( host, Q3Dns::A );
+ d->dns6 = new Q3Dns( host, Q3Dns::Aaaa );
+
+ // try if the address is already available (for faster connecting...)
+ tryConnecting();
+ if ( d->state == HostLookup ) {
+ connect( d->dns4, SIGNAL(resultsReady()),
+ this, SLOT(tryConnecting()) );
+ connect( d->dns6, SIGNAL(resultsReady()),
+ this, SLOT(tryConnecting()) );
+ }
+}
+
+#endif
+
+
+/*!
+ This private slots continues the connection process where
+ connectToHost() leaves off.
+*/
+
+void Q3Socket::tryConnecting()
+{
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting()", name() );
+#endif
+ // ### this ifdef isn't correct - addresses() also does /etc/hosts and
+ // numeric-address-as-string handling.
+#ifndef QT_NO_DNS
+
+ if ( d->dns4 ) {
+ d->l4 = d->dns4->addresses();
+ if ( !d->l4.isEmpty() || !d->dns4->isWorking() ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: host %s, port %d: "
+ "%d IPv4 addresses",
+ name(), d->host.ascii(), d->port, d->l4.count() );
+#endif
+ delete d->dns4;
+ d->dns4 = 0;
+ }
+ }
+
+ if ( d->dns6 ) {
+ d->l6 = d->dns6->addresses();
+ if ( !d->l6.isEmpty() || !d->dns6->isWorking() ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: host %s, port %d: "
+ "%d IPv6 addresses",
+ name(), d->host.ascii(), d->port, d->l6.count() );
+#endif
+ delete d->dns6;
+ d->dns6 = 0;
+ }
+ }
+
+ if ( d->state == HostLookup ) {
+ if ( d->l4.isEmpty() && d->l6.isEmpty() &&
+ !d->dns4 && !d->dns6 ) {
+ // no results and we're not still looking: give up
+ d->state = Idle;
+ emit error( ErrHostNotFound );
+ return;
+ }
+ if ( d->l4.isEmpty() && d->l6.isEmpty() ) {
+ // no results (yet): try again later
+ return;
+ }
+
+ // we've found something. press on with that. if we later find
+ // more, fine.
+ emit hostFound();
+ d->state = Connecting;
+ }
+
+ if ( d->state == Connecting ) {
+ d->addresses += d->l4;
+ d->addresses += d->l6;
+ d->l4.clear();
+ d->l6.clear();
+
+ // try one address at a time, falling back to the next one if
+ // there is a connection failure. (should also support a timeout,
+ // or do multiple TCP-level connects at a time, with staggered
+ // starts to avoid bandwidth waste and cause fewer
+ // "connect-and-abort" errors. but that later.)
+ bool stuck = true;
+ while( stuck ) {
+ stuck = false;
+ if ( d->socket &&
+ d->socket->connect( d->addr, d->port ) == false ) {
+ if ( d->socket->error() == Q3SocketDevice::NoError ) {
+ if ( d->wsn )
+ d->wsn->setEnabled( true );
+ return; // not serious, try again later
+ }
+
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: "
+ "Gave up on IP address %s",
+ name(), d->socket->peerAddress().toString().ascii() );
+#endif
+ delete d->wsn;
+ d->wsn = 0;
+ delete d->rsn;
+ d->rsn = 0;
+ delete d->socket;
+ d->socket = 0;
+
+ if(d->addresses.isEmpty()) {
+ emit error( ErrConnectionRefused );
+ return;
+ }
+ }
+ // if the host has more addresses, try another some.
+ if ( d->socket == 0 && !d->addresses.isEmpty() ) {
+ d->addr = *d->addresses.begin();
+ d->addresses.remove( d->addresses.begin() );
+ d->setSocketDevice( this, 0 );
+ stuck = true;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s)::tryConnecting: Trying IP address %s",
+ name(), d->addr.toString().ascii() );
+#endif
+ }
+ };
+
+ // The socket write notifier will fire when the connection succeeds
+ if ( d->wsn )
+ d->wsn->setEnabled( true );
+ }
+#endif
+}
+
+/*!
+ \enum Q3Socket::Error
+
+ This enum specifies the possible errors:
+ \value ErrConnectionRefused if the connection was refused
+ \value ErrHostNotFound if the host was not found
+ \value ErrSocketRead if a read from the socket failed
+*/
+
+/*!
+ \fn void Q3Socket::error(int error)
+
+ This signal is emitted after an error occurred. The \a error parameter is
+ the \l Error value.
+*/
+
+/*!
+ \fn void Q3Socket::hostFound()
+
+ This signal is emitted after connectToHost() has been called and
+ the host lookup has succeeded.
+
+ \sa connected()
+*/
+
+
+/*!
+ \fn void Q3Socket::connected()
+
+ This signal is emitted after connectToHost() has been called and a
+ connection has been successfully established.
+
+ \sa connectToHost(), connectionClosed()
+*/
+
+
+/*!
+ \fn void Q3Socket::connectionClosed()
+
+ This signal is emitted when the other end has closed the
+ connection. The read buffers may contain buffered input data which
+ you can read after the connection was closed.
+
+ \sa connectToHost(), close()
+*/
+
+
+/*!
+ \fn void Q3Socket::delayedCloseFinished()
+
+ This signal is emitted when a delayed close is finished.
+
+ If you call close() and there is buffered output data to be
+ written, Q3Socket goes into the Q3Socket::Closing state and
+ returns immediately. It will then keep writing to the socket until
+ all the data has been written. Then, the delayedCloseFinished()
+ signal is emitted.
+
+ \sa close()
+*/
+
+
+/*!
+ \fn void Q3Socket::readyRead()
+
+ This signal is emitted every time there is new incoming data.
+
+ Bear in mind that new incoming data is only reported once; if you do not
+ read all the data, this class buffers the data and you can read it later,
+ but no signal is emitted unless new data arrives. A good practice is to
+ read all data in the slot connected to this signal unless you are sure that
+ you need to receive more data to be able to process it.
+
+ \sa readBlock(), readLine(), bytesAvailable()
+*/
+
+
+/*!
+ \fn void Q3Socket::bytesWritten( int nbytes )
+
+ This signal is emitted when data has been written to the network.
+ The \a nbytes parameter specifies how many bytes were written.
+
+ The bytesToWrite() function is often used in the same context; it
+ indicates how many buffered bytes there are left to write.
+
+ \sa writeBlock(), bytesToWrite()
+*/
+
+
+/*!
+ Opens the socket using the specified QIODevice file mode \a m.
+ This function is called automatically when needed and you should
+ not call it yourself.
+
+ \sa close()
+*/
+
+bool Q3Socket::open( OpenMode m )
+{
+ if ( isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "Q3Socket::open: Already open" );
+#endif
+ return false;
+ }
+ QIODevice::setOpenMode( m & ReadWrite );
+ return true;
+}
+
+/*!
+ \fn bool Q3Socket::open(int m)
+ \overload
+*/
+
+/*!
+ Closes the socket.
+
+ The read buffer is cleared.
+
+ If the output buffer is empty, the state is set to \c
+ Q3Socket::Idle and the connection is terminated immediately. If the
+ output buffer still contains data to be written, Q3Socket goes into
+ the Q3Socket::Closing state and the rest of the data will be
+ written. When all of the outgoing data have been written, the
+ state is set to Q3Socket::Idle and the connection is terminated.
+ At this point, the delayedCloseFinished() signal is emitted.
+
+ If you don't want that the data of the output buffer is written, call
+ clearPendingData() before you call close().
+
+ \sa state(), bytesToWrite() clearPendingData()
+*/
+
+void Q3Socket::close()
+{
+ if ( !isOpen() || d->state == Idle ) // already closed
+ return;
+ if ( d->state == Closing )
+ return;
+ if ( !d->rsn || !d->wsn )
+ return;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): close socket", name() );
+#endif
+ if ( d->socket && d->wsize ) { // there's data to be written
+ d->state = Closing;
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ if ( d->wsn )
+ d->wsn->setEnabled( true );
+ d->rba.clear(); // clear incoming data
+ return;
+ }
+ resetStatus();
+ setOpenMode(NotOpen);
+ d->close();
+ d->state = Idle;
+}
+
+
+/*!
+ This function consumes \a nbytes bytes of data from the write
+ buffer.
+*/
+
+bool Q3Socket::consumeWriteBuf( Q_ULONG nbytes )
+{
+ if ( nbytes <= 0 || (qint64)nbytes > d->wsize )
+ return false;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): skipWriteBuf %d bytes", name(), (int)nbytes );
+#endif
+ d->wsize -= nbytes;
+ for ( ;; ) {
+ QByteArray *a = d->wba.first();
+ if ( (qint64)(d->windex + nbytes) >= a->size() ) {
+ nbytes -= a->size() - d->windex;
+ d->wba.remove();
+ d->windex = 0;
+ if ( nbytes == 0 )
+ break;
+ } else {
+ d->windex += nbytes;
+ break;
+ }
+ }
+ return true;
+}
+
+
+
+/*!
+ Implementation of the abstract virtual QIODevice::flush() function.
+ This function always returns true.
+*/
+
+bool Q3Socket::flush()
+{
+ if ( !d->socket )
+ return true;
+ bool osBufferFull = false;
+ int consumed = 0;
+ while ( !osBufferFull && d->state >= Connecting && d->wsize > 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): flush: Write data to the socket", name() );
+#endif
+ QByteArray *a = d->wba.first();
+ int nwritten;
+ int i = 0;
+ if ( (int)a->size() - d->windex < 1460 ) {
+ // Concatenate many smaller blocks. the first may be
+ // partial, but each subsequent block is copied entirely
+ // or not at all. the sizes here are picked so that we
+ // generally won't trigger nagle's algorithm in the tcp
+ // implementation: we concatenate if we'd otherwise send
+ // less than PMTU bytes (we assume PMTU is 1460 bytes),
+ // and concatenate up to the largest payload TCP/IP can
+ // carry. with these precautions, nagle's algorithm
+ // should apply only when really appropriate.
+ QByteArray out( 65536 );
+ int j = d->windex;
+ int s = a->size() - j;
+ while ( a && i+s < (int)out.size() ) {
+ memcpy( out.data()+i, a->data()+j, s );
+ j = 0;
+ i += s;
+ a = d->wba.next();
+ s = a ? a->size() : 0;
+ }
+ nwritten = d->socket->write( out.data(), i );
+ if ( d->wsn )
+ d->wsn->setEnabled( false ); // the QSocketNotifier documentation says so
+ } else {
+ // Big block, write it immediately
+ i = a->size() - d->windex;
+ nwritten = d->socket->write( a->data() + d->windex, i );
+ if ( d->wsn )
+ d->wsn->setEnabled( false ); // the QSocketNotifier documentation says so
+ }
+ if ( nwritten > 0 ) {
+ if ( consumeWriteBuf( nwritten ) )
+ consumed += nwritten;
+ }
+ if ( nwritten < i )
+ osBufferFull = true;
+ }
+ if ( consumed > 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): flush: wrote %d bytes, %d left",
+ name(), consumed, (int)d->wsize );
+#endif
+ emit bytesWritten( consumed );
+ }
+ if ( d->state == Closing && d->wsize == 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): flush: Delayed close done. Terminating.",
+ name() );
+#endif
+ resetStatus();
+ setOpenMode(NotOpen);
+ d->close();
+ d->state = Idle;
+ emit delayedCloseFinished();
+ return true;
+ }
+ if ( !d->socket->isOpen() ) {
+ d->connectionClosed();
+ emit connectionClosed();
+ return true;
+ }
+ if ( d->wsn )
+ d->wsn->setEnabled( d->wsize > 0 ); // write if there's data
+ return true;
+}
+
+
+/*!
+ Returns the number of incoming bytes that can be read right now
+ (like bytesAvailable()).
+*/
+
+QIODevice::Offset Q3Socket::size() const
+{
+ return (Offset)bytesAvailable();
+}
+
+
+/*!
+ Returns the current read index. Since Q3Socket is a sequential
+ device, the current read index is always zero.
+*/
+
+QIODevice::Offset Q3Socket::at() const
+{
+ return 0;
+}
+
+
+/*!
+ \overload
+
+ Moves the read index forward to \a index and returns true if the
+ operation was successful; otherwise returns false. Moving the
+ index forward means skipping incoming data.
+*/
+
+bool Q3Socket::at( Offset index )
+{
+ if ( index > d->rba.size() )
+ return false;
+ d->rba.consumeBytes( (Q_ULONG)index, 0 ); // throw away data 0..index-1
+ // After we read data from our internal buffer, if we use the
+ // setReadBufferSize() to limit our buffer, we might now be able to
+ // read more data in our buffer. So enable the read socket notifier,
+ // but do this only if we are not in a slot connected to the
+ // readyRead() signal since this might cause a bad recursive behavior.
+ // We can test for this condition by looking at the
+ // sn_read_alreadyCalled flag.
+ if ( d->rsn && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) == -1 )
+ d->rsn->setEnabled( true );
+ return true;
+}
+
+
+/*!
+ Returns true if there is no more data to read; otherwise returns false.
+*/
+
+bool Q3Socket::atEnd() const
+{
+ if ( d->socket == 0 )
+ return true;
+ Q3Socket * that = (Q3Socket *)this;
+ if ( that->d->socket->bytesAvailable() ) // a little slow, perhaps...
+ that->sn_read();
+ return that->d->rba.size() == 0;
+}
+
+
+/*!
+ Returns the number of incoming bytes that can be read, i.e. the
+ size of the input buffer. Equivalent to size().
+
+ \sa bytesToWrite()
+*/
+
+qint64 Q3Socket::bytesAvailable() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ Q3Socket * that = (Q3Socket *)this;
+ if ( that->d->socket->bytesAvailable() ) // a little slow, perhaps...
+ (void)that->sn_read();
+ return that->d->rba.size() + QIODevice::bytesAvailable();
+}
+
+
+/*!
+ Wait up to \a msecs milliseconds for more data to be available.
+
+ If \a msecs is -1 the call will block indefinitely.
+
+ Returns the number of bytes available.
+
+ If \a timeout is non-null and no error occurred (i.e. it does not
+ return -1): this function sets *\a timeout to true, if the reason
+ for returning was that the timeout was reached; otherwise it sets
+ *\a timeout to false. This is useful to find out if the peer
+ closed the connection.
+
+ \warning This is a blocking call and should be avoided in event
+ driven applications.
+
+ \sa bytesAvailable()
+*/
+
+Q_ULONG Q3Socket::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( d->socket == 0 )
+ return 0;
+ Q3Socket * that = (Q3Socket *)this;
+ if ( that->d->socket->waitForMore( msecs, timeout ) > 0 )
+ (void)that->sn_read( true );
+ return that->d->rba.size();
+}
+
+/*! \overload
+*/
+
+Q_ULONG Q3Socket::waitForMore( int msecs ) const
+{
+ return waitForMore( msecs, 0 );
+}
+
+/*!
+ Returns the number of bytes that are waiting to be written, i.e.
+ the size of the output buffer.
+
+ \sa bytesAvailable() clearPendingData()
+*/
+
+qint64 Q3Socket::bytesToWrite() const
+{
+ return d->wsize;
+}
+
+/*!
+ Deletes the data that is waiting to be written. This is useful if you want
+ to close the socket without waiting for all the data to be written.
+
+ \sa bytesToWrite() close() delayedCloseFinished()
+*/
+
+void Q3Socket::clearPendingData()
+{
+ d->wba.clear();
+ d->windex = d->wsize = 0;
+}
+
+/*!
+ Reads \a maxlen bytes from the socket into \a data and returns the
+ number of bytes read. Returns -1 if an error occurred.
+*/
+
+qint64 Q3Socket::readData( char *data, qint64 maxlen )
+{
+ if ( data == 0 && maxlen != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3Socket::readBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "Q3Socket::readBlock: Socket is not open" );
+#endif
+ return -1;
+ }
+ if ( maxlen >= d->rba.size() )
+ maxlen = d->rba.size();
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): readBlock %d bytes", name(), (int)maxlen );
+#endif
+ d->rba.consumeBytes( maxlen, data );
+ // After we read data from our internal buffer, if we use the
+ // setReadBufferSize() to limit our buffer, we might now be able to
+ // read more data in our buffer. So enable the read socket notifier,
+ // but do this only if we are not in a slot connected to the
+ // readyRead() signal since this might cause a bad recursive behavior.
+ // We can test for this condition by looking at the
+ // sn_read_alreadyCalled flag.
+ if ( d->rsn && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) == -1 )
+ d->rsn->setEnabled( true );
+ return maxlen;
+}
+
+
+/*!
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+*/
+
+qint64 Q3Socket::writeData( const char *data, qint64 len )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && len != 0 ) {
+ qWarning( "Q3Socket::writeBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "Q3Socket::writeBlock: Socket is not open" );
+ return -1;
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( d->state == Closing ) {
+ qWarning( "Q3Socket::writeBlock: Cannot write, socket is closing" );
+ }
+#endif
+ if ( len == 0 || d->state == Closing || d->state == Idle )
+ return 0;
+ QByteArray *a = d->wba.last();
+
+ // next bit is sensitive. if we're writing really small chunks,
+ // try to buffer up since system calls are expensive, and nagle's
+ // algorithm is even more expensive. but if anything even
+ // remotely large is being written, try to issue a write at once.
+
+ bool writeNow = ( d->wsize + len >= 1400 || len > 512 );
+
+ if ( a && a->size() + len < 128 ) {
+ // small buffer, resize
+ int i = a->size();
+ a->resize( i+len );
+ memcpy( a->data()+i, data, len );
+ } else {
+ // append new buffer
+ a = new QByteArray( len );
+ memcpy( a->data(), data, len );
+ d->wba.append( a );
+ }
+ d->wsize += len;
+ if ( writeNow )
+ flush();
+ else if ( d->wsn )
+ d->wsn->setEnabled( true );
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): writeBlock %d bytes", name(), (int)len );
+#endif
+ return len;
+}
+
+
+/*!
+ Reads a single byte/character from the internal read buffer.
+ Returns the byte/character read, or -1 if there is nothing to be
+ read.
+
+ \sa bytesAvailable(), putch()
+*/
+
+int Q3Socket::getch()
+{
+ if ( isOpen() && d->rba.size() > 0 ) {
+ uchar c;
+ d->rba.consumeBytes( 1, (char*)&c );
+ // After we read data from our internal buffer, if we use the
+ // setReadBufferSize() to limit our buffer, we might now be able to
+ // read more data in our buffer. So enable the read socket notifier,
+ // but do this only if we are not in a slot connected to the
+ // readyRead() signal since this might cause a bad recursive behavior.
+ // We can test for this condition by looking at the
+ // sn_read_alreadyCalled flag.
+ if ( d->rsn && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) == -1 )
+ d->rsn->setEnabled( true );
+ return c;
+ }
+ return -1;
+}
+
+
+/*!
+ Writes the character \a ch to the output buffer.
+
+ Returns \a ch, or -1 if an error occurred.
+
+ \sa getch()
+*/
+
+int Q3Socket::putch( int ch )
+{
+ char buf[2];
+ buf[0] = ch;
+ return writeBlock(buf, 1) == 1 ? ch : -1;
+}
+
+
+/*!
+ This implementation of the virtual function QIODevice::ungetch()
+ prepends the character \a ch to the read buffer so that the next
+ read returns this character as the first character of the output.
+*/
+
+int Q3Socket::ungetch( int ch )
+{
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "Q3Socket::ungetch: Socket not open" );
+ return -1;
+ }
+#endif
+ return d->rba.ungetch( ch );
+}
+
+
+/*!
+ Returns true if it's possible to read an entire line of text from
+ this socket at this time; otherwise returns false.
+
+ Note that if the peer closes the connection unexpectedly, this
+ function returns false. This means that loops such as this won't
+ work:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3socket.cpp 0
+
+ \sa readLine()
+*/
+
+bool Q3Socket::canReadLine() const
+{
+ if ( ((Q3Socket*)this)->d->rba.scanNewline( 0 ) )
+ return true;
+ return ( bytesAvailable() > 0 &&
+ (((Q3Socket*)this)->d->rba.scanNewline( 0 ) || QIODevice::canReadLine()) );
+}
+
+/*!
+ \internal
+ Internal slot for handling socket read notifications.
+
+ This function has can usually only be entered once (i.e. no
+ recursive calls). If the argument \a force is true, the function
+ is executed, but no readyRead() signals are emitted. This
+ behaviour is useful for the waitForMore() function, so that it is
+ possible to call waitForMore() in a slot connected to the
+ readyRead() signal.
+*/
+
+void Q3Socket::sn_read( bool force )
+{
+ Q_LONG maxToRead = 0;
+ if ( d->readBufferSize > 0 ) {
+ maxToRead = d->readBufferSize - d->rba.size();
+ if ( maxToRead <= 0 ) {
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ return;
+ }
+ }
+
+ // Use Q3SocketPrivate::sn_read_alreadyCalled to avoid recursive calls of
+ // sn_read() (and as a result avoid emitting the readyRead() signal in a
+ // slot for readyRead(), if you use bytesAvailable()).
+ if ( !force && Q3SocketPrivate::sn_read_alreadyCalled.findRef(this) != -1 )
+ return;
+ Q3SocketPrivate::sn_read_alreadyCalled.append( this );
+
+ char buf[4096];
+ Q_LONG nbytes = d->socket->bytesAvailable();
+ Q_LONG nread;
+ QByteArray *a = 0;
+
+ if ( state() == Connecting ) {
+ if ( nbytes > 0 ) {
+ tryConnection();
+ } else {
+ // nothing to do, nothing to care about
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ }
+ if ( state() == Idle ) {
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+
+ if ( nbytes <= 0 ) { // connection closed?
+ // On Windows this may happen when the connection is still open.
+ // This happens when the system is heavily loaded and we have
+ // read all the data on the socket before a new WSAAsyncSelect
+ // event is processed. A new read operation would then block.
+ // This code is also useful when Q3Socket is used without an
+ // event loop.
+ nread = d->socket->readBlock( buf, maxToRead ? QMIN((Q_LONG)sizeof(buf),maxToRead) : sizeof(buf) );
+ if ( nread == 0 ) { // really closed
+ if ( !d->socket->isOpen() ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_read: Connection closed", name() );
+#endif
+ d->connectionClosed();
+ emit connectionClosed();
+ }
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ } else {
+ if ( nread < 0 ) {
+ if ( d->socket->error() == Q3SocketDevice::NoError ) {
+ // all is fine
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+#if defined(Q3SOCKET_DEBUG)
+ qWarning( "Q3Socket::sn_read (%s): Close error", name() );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ emit error( ErrSocketRead );
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ a = new QByteArray( nread );
+ memcpy( a->data(), buf, nread );
+ }
+
+ } else { // data to be read
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_read: %ld incoming bytes", name(), nbytes );
+#endif
+ if ( nbytes > (int)sizeof(buf) ) {
+ // big
+ a = new QByteArray( nbytes );
+ nread = d->socket->readBlock( a->data(), maxToRead ? QMIN(nbytes,maxToRead) : nbytes );
+ } else {
+ a = 0;
+ nread = d->socket->readBlock( buf, maxToRead ? QMIN((Q_LONG)sizeof(buf),maxToRead) : sizeof(buf) );
+ if ( nread > 0 ) {
+ // ##### could setRawData
+ a = new QByteArray( nread );
+ memcpy( a->data(), buf, nread );
+ }
+ }
+ if ( nread == 0 ) {
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_read: Connection closed", name() );
+#endif
+ // ### we should rather ask the socket device if it is closed
+ d->connectionClosed();
+ emit connectionClosed();
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ delete a;
+ return;
+ } else if ( nread < 0 ) {
+ delete a;
+
+ if ( d->socket->error() == Q3SocketDevice::NoError ) {
+ // all is fine
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+#if defined(QT_CHECK_RANGE)
+ qWarning( "Q3Socket::sn_read: Read error" );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ emit error( ErrSocketRead );
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ if ( nread != (int)a->size() ) { // unexpected
+#if defined(CHECK_RANGE) && !defined(Q_OS_WIN32)
+ qWarning( "Q3Socket::sn_read: Unexpected short read" );
+#endif
+ a->resize( nread );
+ }
+ }
+ d->rba.append( a );
+ if ( !force ) {
+ if ( d->rsn )
+ d->rsn->setEnabled( false );
+ emit readyRead();
+ if ( d->rsn )
+ d->rsn->setEnabled( true );
+ }
+
+ Q3SocketPrivate::sn_read_alreadyCalled.removeRef( this );
+}
+
+
+/*!
+ \internal
+ Internal slot for handling socket write notifications.
+*/
+
+void Q3Socket::sn_write()
+{
+ if ( d->state == Connecting ) // connection established?
+ tryConnection();
+ flush();
+}
+
+void Q3Socket::emitErrorConnectionRefused()
+{
+ emit error( ErrConnectionRefused );
+}
+
+void Q3Socket::tryConnection()
+{
+ if ( d->socket->connect( d->addr, d->port ) ) {
+ d->state = Connected;
+#if defined(Q3SOCKET_DEBUG)
+ qDebug( "Q3Socket (%s): sn_write: Got connection to %s",
+ name(), peerName().ascii() );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( true );
+ emit connected();
+ } else {
+ d->state = Idle;
+ QTimer::singleShot( 0, this, SLOT(emitErrorConnectionRefused()) );
+ return;
+ }
+}
+
+
+/*!
+ Returns the socket number, or -1 if there is no socket at the moment.
+*/
+
+int Q3Socket::socket() const
+{
+ if ( d->socket == 0 )
+ return -1;
+ return d->socket->socket();
+}
+
+/*!
+ Sets the socket to use \a socket and the state() to \c Connected.
+ The socket must already be connected.
+
+ This allows us to use the Q3Socket class as a wrapper for other
+ socket types (e.g. Unix Domain Sockets).
+*/
+
+void Q3Socket::setSocket( int socket )
+{
+ setSocketIntern( socket );
+ d->state = Connection;
+ d->rsn->setEnabled( true );
+}
+
+
+/*!
+ Sets the socket to \a socket. This is used by both setSocket() and
+ connectToHost() and can also be used on unconnected sockets.
+*/
+
+void Q3Socket::setSocketIntern( int socket )
+{
+ if ( state() != Idle ) {
+ clearPendingData();
+ close();
+ }
+ Q_ULONG oldBufferSize = d ? d->readBufferSize : 0;
+ delete d;
+
+ d = new Q3SocketPrivate;
+ if (oldBufferSize)
+ d->readBufferSize = oldBufferSize;
+ if ( socket >= 0 ) {
+ Q3SocketDevice *sd = new Q3SocketDevice( socket, Q3SocketDevice::Stream );
+ sd->setBlocking( false );
+ sd->setAddressReusable( true );
+ d->setSocketDevice( this, sd );
+ }
+ d->state = Idle;
+
+ // Initialize the IO device flags
+ resetStatus();
+ open( IO_ReadWrite );
+
+ // hm... this is not very nice.
+ d->host.clear();
+ d->port = 0;
+#ifndef QT_NO_DNS
+ delete d->dns4;
+ d->dns4 = 0;
+ delete d->dns6;
+ d->dns6 = 0;
+#endif
+}
+
+
+/*!
+ Returns the host port number of this socket, in native byte order.
+*/
+
+Q_UINT16 Q3Socket::port() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ return d->socket->port();
+}
+
+
+/*!
+ Returns the peer's host port number, normally as specified to the
+ connectToHost() function. If none has been set, this function
+ returns 0.
+
+ Note that Qt always uses native byte order, i.e. 67 is 67 in Qt;
+ there is no need to call htons().
+*/
+
+Q_UINT16 Q3Socket::peerPort() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ return d->socket->peerPort();
+}
+
+
+/*!
+ Returns the host address of this socket. (This is normally the
+ main IP address of the host, but can be e.g. 127.0.0.1 for
+ connections to localhost.)
+*/
+
+QHostAddress Q3Socket::address() const
+{
+ if ( d->socket == 0 ) {
+ QHostAddress tmp;
+ return tmp;
+ }
+ return d->socket->address();
+}
+
+
+/*!
+ Returns the address of the connected peer if the socket is in
+ Connected state; otherwise an empty QHostAddress is returned.
+*/
+
+QHostAddress Q3Socket::peerAddress() const
+{
+ if ( d->socket == 0 ) {
+ QHostAddress tmp;
+ return tmp;
+ }
+ return d->socket->peerAddress();
+}
+
+
+/*!
+ Returns the host name as specified to the connectToHost()
+ function. An empty string is returned if none has been set.
+*/
+
+QString Q3Socket::peerName() const
+{
+ return d->host;
+}
+
+/*!
+ Sets the size of the Q3Socket's internal read buffer to \a bufSize.
+
+ Usually Q3Socket reads all data that is available from the operating
+ system's socket. If the buffer size is limited to a certain size, this
+ means that the Q3Socket class doesn't buffer more than this size of data.
+
+ If the size of the read buffer is 0, the read buffer is unlimited and all
+ incoming data is buffered. This is the default.
+
+ If you read the data in the readyRead() signal, you shouldn't use this
+ option since it might slow down your program unnecessary. This option is
+ useful if you only need to read the data at certain points in time, like in
+ a realtime streaming application.
+
+ \sa readBufferSize()
+*/
+
+void Q3Socket::setReadBufferSize( Q_ULONG bufSize )
+{
+ d->readBufferSize = bufSize;
+}
+
+/*!
+ Returns the size of the read buffer.
+
+ \sa setReadBufferSize()
+*/
+
+Q_ULONG Q3Socket::readBufferSize() const
+{
+ return d->readBufferSize;
+}
+
+/*!
+ \fn bool Q3Socket::isSequential() const
+ \internal
+*/
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3socket.h b/src/qt3support/network/q3socket.h
new file mode 100644
index 0000000000..79224062ae
--- /dev/null
+++ b/src/qt3support/network/q3socket.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SOCKET_H
+#define Q3SOCKET_H
+
+#include <QtCore/qiodevice.h>
+#include <QtNetwork/qhostaddress.h> // int->QHostAddress conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+class Q3SocketPrivate;
+class Q3SocketDevice;
+
+class Q_COMPAT_EXPORT Q3Socket : public QIODevice
+{
+ Q_OBJECT
+public:
+ enum Error {
+ ErrConnectionRefused,
+ ErrHostNotFound,
+ ErrSocketRead
+ };
+
+ Q3Socket( QObject *parent=0, const char *name=0 );
+ virtual ~Q3Socket();
+
+ enum State { Idle, HostLookup, Connecting,
+ Connected, Closing,
+ Connection=Connected };
+ State state() const;
+
+ int socket() const;
+ virtual void setSocket( int );
+
+ Q3SocketDevice *socketDevice();
+ virtual void setSocketDevice( Q3SocketDevice * );
+
+#ifndef QT_NO_DNS
+ virtual void connectToHost( const QString &host, Q_UINT16 port );
+#endif
+ QString peerName() const;
+
+ // Implementation of QIODevice abstract virtual functions
+ bool open( OpenMode mode );
+ bool open(int mode) { return open((OpenMode)mode); }
+ void close();
+ bool flush();
+ Offset size() const;
+ Offset at() const;
+ bool at( Offset );
+ bool atEnd() const;
+
+ qint64 bytesAvailable() const;
+ Q_ULONG waitForMore( int msecs, bool *timeout ) const;
+ Q_ULONG waitForMore( int msecs ) const; // ### Qt 4.0: merge the two overloads
+ qint64 bytesToWrite() const;
+ void clearPendingData();
+
+ int getch();
+ int putch( int );
+ int ungetch(int);
+
+ bool canReadLine() const;
+
+ Q_UINT16 port() const;
+ Q_UINT16 peerPort() const;
+ QHostAddress address() const;
+ QHostAddress peerAddress() const;
+
+ void setReadBufferSize( Q_ULONG );
+ Q_ULONG readBufferSize() const;
+
+ inline bool isSequential() const { return true; }
+
+Q_SIGNALS:
+ void hostFound();
+ void connected();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void bytesWritten( int nbytes );
+ void error( int );
+
+protected Q_SLOTS:
+ virtual void sn_read( bool force=false );
+ virtual void sn_write();
+
+protected:
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private Q_SLOTS:
+ void tryConnecting();
+ void emitErrorConnectionRefused();
+
+private:
+ Q3SocketPrivate *d;
+
+ bool consumeWriteBuf( Q_ULONG nbytes );
+ void tryConnection();
+ void setSocketIntern( int socket );
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3Socket( const Q3Socket & );
+ Q3Socket &operator=( const Q3Socket & );
+#endif
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SOCKET_H
diff --git a/src/qt3support/network/q3socketdevice.cpp b/src/qt3support/network/q3socketdevice.cpp
new file mode 100644
index 0000000000..7e53163740
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice.cpp
@@ -0,0 +1,757 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socketdevice.h"
+#ifndef QT_NO_NETWORK
+
+#include "qwindowdefs.h"
+#include <string.h>
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3SOCKETDEVICE_DEBUG
+
+
+class Q3SocketDevicePrivate
+{
+public:
+ Q3SocketDevicePrivate( Q3SocketDevice::Protocol p )
+ : protocol(p)
+ { }
+
+ Q3SocketDevice::Protocol protocol;
+};
+
+
+/*!
+ \class Q3SocketDevice
+ \brief The Q3SocketDevice class provides a platform-independent low-level socket API.
+
+ \compat
+ \reentrant
+
+ This class provides a low level API for working with sockets. Users of
+ this class are assumed to have networking experience. For most users the
+ Q3Socket class provides a much easier and high level alternative, but
+ certain things (like UDP) can't be done with Q3Socket and if you need a
+ platform-independent API for those, Q3SocketDevice is the right choice.
+
+ The essential purpose of the class is to provide a QIODevice that
+ works on sockets, wrapped in a platform-independent API.
+
+ When calling connect() or bind(), Q3SocketDevice detects the
+ protocol family (IPv4, IPv6) automatically. Passing the protocol
+ family to Q3SocketDevice's constructor or to setSocket() forces
+ creation of a socket device of a specific protocol. If not set, the
+ protocol will be detected at the first call to connect() or bind().
+
+ \sa Q3Socket, QSocketNotifier, QHostAddress
+*/
+
+
+/*!
+ \enum Q3SocketDevice::Protocol
+
+ This enum type describes the protocol family of the socket. Possible values
+ are:
+
+ \value IPv4 The socket is an IPv4 socket.
+ \value IPv6 The socket is an IPv6 socket.
+ \value Unknown The protocol family of the socket is not known. This can
+ happen if you use Q3SocketDevice with an already existing socket; it
+ tries to determine the protocol family, but this can fail if the
+ protocol family is not known to Q3SocketDevice.
+
+ \sa protocol() setSocket()
+*/
+
+/*!
+ \enum Q3SocketDevice::Error
+
+ This enum type describes the error states of Q3SocketDevice.
+
+ \value NoError No error has occurred.
+
+ \value AlreadyBound The device is already bound, according to bind().
+
+ \value Inaccessible The operating system or firewall prohibited
+ the action.
+
+ \value NoResources The operating system ran out of a resource.
+
+ \value InternalError An internal error occurred in Q3SocketDevice.
+
+ \value Impossible An attempt was made to do something which makes
+ no sense. For example:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3socketdevice.cpp 0
+ The libc ::close() closes the socket, but Q3SocketDevice is not aware
+ of this. So when you call writeBlock(), the impossible happens.
+
+ \value NoFiles The operating system will not let Q3SocketDevice open
+ another file.
+
+ \value ConnectionRefused A connection attempt was rejected by the
+ peer.
+
+ \value NetworkFailure There is a network failure.
+
+ \value UnknownError The operating system did something
+ unexpected.
+
+ \omitvalue Bug
+*/
+
+/*!
+ \enum Q3SocketDevice::Type
+
+ This enum type describes the type of the socket:
+ \value Stream a stream socket (TCP, usually)
+ \value Datagram a datagram socket (UDP, usually)
+*/
+
+
+/*!
+ Creates a Q3SocketDevice object for the existing socket \a socket.
+
+ The \a type argument must match the actual socket type; use \c
+ Q3SocketDevice::Stream for a reliable, connection-oriented TCP
+ socket, or Q3SocketDevice::Datagram for an unreliable,
+ connectionless UDP socket.
+*/
+Q3SocketDevice::Q3SocketDevice( int socket, Type type )
+ : fd( socket ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new Q3SocketDevicePrivate(Unknown))
+{
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Created Q3SocketDevice %p (socket %x, type %d)",
+ this, socket, type );
+#endif
+ init();
+ setSocket( socket, type );
+}
+
+/*!
+ Creates a Q3SocketDevice object for a stream or datagram socket.
+
+ The \a type argument must be either Q3SocketDevice::Stream for a
+ reliable, connection-oriented TCP socket, or \c
+ Q3SocketDevice::Datagram for an unreliable UDP socket.
+
+ The socket is created as an IPv4 socket.
+
+ \sa blocking() protocol()
+*/
+Q3SocketDevice::Q3SocketDevice( Type type )
+ : fd( -1 ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new Q3SocketDevicePrivate(IPv4))
+{
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Created Q3SocketDevice object %p, type %d",
+ this, type );
+#endif
+ init();
+ setSocket( createNewSocket(), type );
+}
+
+/*!
+ Creates a Q3SocketDevice object for a stream or datagram socket.
+
+ The \a type argument must be either Q3SocketDevice::Stream for a
+ reliable, connection-oriented TCP socket, or \c
+ Q3SocketDevice::Datagram for an unreliable UDP socket.
+
+ The \a protocol indicates whether the socket should be of type IPv4
+ or IPv6. Passing \c Unknown is not meaningful in this context and you
+ should avoid using (it creates an IPv4 socket, but your code is not easily
+ readable).
+
+ The argument \a dummy is necessary for compatibility with some
+ compilers.
+
+ \sa blocking() protocol()
+*/
+Q3SocketDevice::Q3SocketDevice( Type type, Protocol protocol, int )
+ : fd( -1 ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new Q3SocketDevicePrivate(protocol))
+{
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Created Q3SocketDevice object %p, type %d",
+ this, type );
+#endif
+ init();
+ setSocket( createNewSocket(), type );
+}
+
+/*!
+ Destroys the socket device and closes the socket if it is open.
+*/
+Q3SocketDevice::~Q3SocketDevice()
+{
+ close();
+ delete d;
+ d = 0;
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: Destroyed Q3SocketDevice %p", this );
+#endif
+}
+
+
+/*!
+ Returns true if this is a valid socket; otherwise returns false.
+
+ \sa socket()
+*/
+bool Q3SocketDevice::isValid() const
+{
+ return fd != -1;
+}
+
+
+/*!
+ \fn Type Q3SocketDevice::type() const
+
+ Returns the socket type which is either Q3SocketDevice::Stream
+ or Q3SocketDevice::Datagram.
+
+ \sa socket()
+*/
+Q3SocketDevice::Type Q3SocketDevice::type() const
+{
+ return t;
+}
+
+/*!
+ Returns the socket's protocol family, which is one of \c Unknown, \c IPv4,
+ or \c IPv6.
+
+ Q3SocketDevice either creates a socket with a well known protocol family or
+ it uses an already existing socket. In the first case, this function
+ returns the protocol family it was constructed with. In the second case, it
+ tries to determine the protocol family of the socket; if this fails, it
+ returns \c Unknown.
+
+ \sa Protocol setSocket()
+*/
+Q3SocketDevice::Protocol Q3SocketDevice::protocol() const
+{
+ if ( d->protocol == Unknown )
+ d->protocol = getProtocol();
+ return d->protocol;
+}
+
+/*!
+ Returns the socket number, or -1 if it is an invalid socket.
+
+ \sa isValid(), type()
+*/
+int Q3SocketDevice::socket() const
+{
+ return fd;
+}
+
+
+/*!
+ Sets the socket device to operate on the existing socket \a
+ socket.
+
+ The \a type argument must match the actual socket type; use \c
+ Q3SocketDevice::Stream for a reliable, connection-oriented TCP
+ socket, or Q3SocketDevice::Datagram for an unreliable,
+ connectionless UDP socket.
+
+ Any existing socket is closed.
+
+ \sa isValid(), close()
+*/
+void Q3SocketDevice::setSocket( int socket, Type type )
+{
+ if ( fd != -1 ) // close any open socket
+ close();
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::setSocket: socket %x, type %d", socket, type );
+#endif
+ t = type;
+ fd = socket;
+ d->protocol = Unknown;
+ e = NoError;
+ resetStatus();
+ open( ReadWrite );
+ fetchConnectionParameters();
+}
+
+
+/*!
+ Opens the socket using the specified QIODevice file \a mode. This
+ function is called from the Q3SocketDevice constructors and from
+ the setSocket() function. You should not call it yourself.
+
+ \sa close()
+*/
+bool Q3SocketDevice::open( OpenMode mode )
+{
+ if ( isOpen() || !isValid() )
+ return false;
+#if defined(Q3SOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::open: mode %x", mode );
+#endif
+ setOpenMode( (mode & ReadWrite) | Unbuffered );
+ return true;
+}
+
+/*!
+ \fn bool Q3SocketDevice::open(int mode)
+ \overload
+*/
+/*!
+ The current Q3SocketDevice implementation does not buffer at all,
+ so this is a no-op. This function always returns true.
+*/
+bool Q3SocketDevice::flush()
+{
+ return true;
+}
+
+
+/*!
+ \reimp
+
+ The size is meaningless for a socket, therefore this function returns 0.
+*/
+QIODevice::Offset Q3SocketDevice::size() const
+{
+ return 0;
+}
+
+
+/*!
+ The read/write index is meaningless for a socket, therefore this
+ function returns 0.
+*/
+QIODevice::Offset Q3SocketDevice::at() const
+{
+ return 0;
+}
+
+
+/*!
+ The read/write index is meaningless for a socket, therefore this
+ function does nothing and returns true.
+
+ The \a offset parameter is ignored.
+*/
+bool Q3SocketDevice::at( Offset /* offset */ )
+{
+ return true;
+}
+
+
+/*!
+ \reimp
+
+ Returns true if no data is currently available at the socket;
+ otherwise returns false.
+*/
+bool Q3SocketDevice::atEnd() const
+{
+ return bytesAvailable() <= 0;
+}
+
+/*!
+ Returns true if the address of this socket can be used by other
+ sockets at the same time, and false if this socket claims
+ exclusive ownership.
+
+ \sa setAddressReusable()
+*/
+bool Q3SocketDevice::addressReusable() const
+{
+ return option( ReuseAddress );
+}
+
+
+/*!
+ Sets the address of this socket to be usable by other sockets too
+ if \a enable is true, and to be used exclusively by this socket if
+ \a enable is false.
+
+ When a socket is reusable, other sockets can use the same port
+ number (and IP address), which is generally useful. Of course
+ other sockets cannot use the same
+ (address,port,peer-address,peer-port) 4-tuple as this socket, so
+ there is no risk of confusing the two TCP connections.
+
+ \sa addressReusable()
+*/
+void Q3SocketDevice::setAddressReusable( bool enable )
+{
+ setOption( ReuseAddress, enable );
+}
+
+
+/*!
+ Returns the size of the operating system receive buffer.
+
+ \sa setReceiveBufferSize()
+*/
+int Q3SocketDevice::receiveBufferSize() const
+{
+ return option( ReceiveBuffer );
+}
+
+
+/*!
+ Sets the size of the operating system receive buffer to \a size.
+
+ The operating system receive buffer size effectively limits two
+ things: how much data can be in transit at any one moment, and how
+ much data can be received in one iteration of the main event loop.
+
+ The default is operating system-dependent. A socket that receives
+ large amounts of data is probably best with a buffer size of
+ 49152.
+*/
+void Q3SocketDevice::setReceiveBufferSize( uint size )
+{
+ setOption( ReceiveBuffer, size );
+}
+
+
+/*!
+ Returns the size of the operating system send buffer.
+
+ \sa setSendBufferSize()
+*/
+int Q3SocketDevice::sendBufferSize() const
+{
+ return option( SendBuffer );
+}
+
+
+/*!
+ Sets the size of the operating system send buffer to \a size.
+
+ The operating system send buffer size effectively limits how much
+ data can be in transit at any one moment.
+
+ The default is operating system-dependent. A socket that sends
+ large amounts of data is probably best with a buffer size of
+ 49152.
+*/
+void Q3SocketDevice::setSendBufferSize( uint size )
+{
+ setOption( SendBuffer, size );
+}
+
+
+/*!
+ Returns the port number of this socket device. This may be 0 for a
+ while, but is set to something sensible as soon as a sensible
+ value is available.
+
+ Note that Qt always uses native byte order, i.e. 67 is 67 in Qt;
+ there is no need to call htons().
+*/
+quint16 Q3SocketDevice::port() const
+{
+ return p;
+}
+
+
+/*!
+ Returns the address of this socket device. This may be 0.0.0.0 for
+ a while, but is set to something sensible as soon as a sensible
+ value is available.
+*/
+QHostAddress Q3SocketDevice::address() const
+{
+ return a;
+}
+
+
+/*!
+ Returns the first error seen.
+*/
+Q3SocketDevice::Error Q3SocketDevice::error() const
+{
+ return e;
+}
+
+
+/*!
+ Allows subclasses to set the error state to \a err.
+*/
+void Q3SocketDevice::setError( Error err )
+{
+ e = err;
+}
+
+/*! \fn Q3SocketDevice::readBlock(char *data, Q_ULONG maxlen)
+
+ Reads \a maxlen bytes from the socket into \a data and returns the
+ number of bytes read. Returns -1 if an error occurred. Returning 0
+ is not an error. For Stream sockets, 0 is returned when the remote
+ host closes the connection. For Datagram sockets, 0 is a valid
+ datagram size.
+*/
+
+/*! \fn Q3SocketDevice::writeBlock(const char *data, Q_ULONG len)
+
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+
+ This is used for Q3SocketDevice::Stream sockets.
+*/
+
+/*!
+ \fn Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const QHostAddress & host, Q_UINT16 port )
+ \overload
+
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+
+ This is used for Q3SocketDevice::Datagram sockets. You must
+ specify the \a host and \a port of the destination of the data.
+*/
+
+/*!
+ \fn bool Q3SocketDevice::isSequential() const
+ \internal
+*/
+
+/*!
+ \fn qint64 Q3SocketDevice::readData( char *data, qint64 maxlen )
+
+ Reads \a maxlen bytes from the socket into \a data and returns the
+ number of bytes read. Returns -1 if an error occurred.
+*/
+
+/*!
+ \fn int Q3SocketDevice::createNewSocket()
+
+ Creates a new socket identifier. Returns -1 if there is a failure
+ to create the new identifier; error() explains why.
+
+ \sa setSocket()
+*/
+
+/*!
+ \fn void Q3SocketDevice::close()
+ \reimp
+
+ Closes the socket and sets the socket identifier to -1 (invalid).
+
+ (This function ignores errors; if there are any then a file
+ descriptor leakage might result. As far as we know, the only error
+ that can arise is EBADF, and that would of course not cause
+ leakage. There may be OS-specific errors that we haven't come
+ across, however.)
+
+ \sa open()
+*/
+
+/*!
+ \fn bool Q3SocketDevice::blocking() const
+
+ Returns true if the socket is valid and in blocking mode;
+ otherwise returns false.
+
+ Note that this function does not set error().
+
+ \warning On Windows, this function always returns true since the
+ ioctlsocket() function is broken.
+
+ \sa setBlocking(), isValid()
+*/
+
+/*!
+ \fn void Q3SocketDevice::setBlocking( bool enable )
+
+ Makes the socket blocking if \a enable is true or nonblocking if
+ \a enable is false.
+
+ Sockets are blocking by default, but we recommend using
+ nonblocking socket operations, especially for GUI programs that
+ need to be responsive.
+
+ \warning On Windows, this function should be used with care since
+ whenever you use a QSocketNotifier on Windows, the socket is
+ immediately made nonblocking.
+
+ \sa blocking(), isValid()
+*/
+
+/*!
+ \fn int Q3SocketDevice::option( Option opt ) const
+
+ Returns the value of the socket option \a opt.
+*/
+
+/*!
+ \fn void Q3SocketDevice::setOption( Option opt, int v )
+
+ Sets the socket option \a opt to \a v.
+*/
+
+/*!
+ \fn bool Q3SocketDevice::connect( const QHostAddress &addr, Q_UINT16 port )
+
+ Connects to the IP address and port specified by \a addr and \a
+ port. Returns true if it establishes a connection; otherwise returns false.
+ If it returns false, error() explains why.
+
+ Note that error() commonly returns NoError for non-blocking
+ sockets; this just means that you can call connect() again in a
+ little while and it'll probably succeed.
+*/
+
+/*!
+ \fn bool Q3SocketDevice::bind( const QHostAddress &address, Q_UINT16 port )
+
+ Assigns a name to an unnamed socket. The name is the host address
+ \a address and the port number \a port. If the operation succeeds,
+ bind() returns true; otherwise it returns false without changing
+ what port() and address() return.
+
+ bind() is used by servers for setting up incoming connections.
+ Call bind() before listen().
+*/
+
+/*!
+ \fn bool Q3SocketDevice::listen( int backlog )
+
+ Specifies how many pending connections a server socket can have.
+ Returns true if the operation was successful; otherwise returns
+ false. A \a backlog value of 50 is quite common.
+
+ The listen() call only applies to sockets where type() is \c
+ Stream, i.e. not to \c Datagram sockets. listen() must not be
+ called before bind() or after accept().
+
+ \sa bind(), accept()
+*/
+
+/*!
+ \fn int Q3SocketDevice::accept()
+
+ Extracts the first connection from the queue of pending
+ connections for this socket and returns a new socket identifier.
+ Returns -1 if the operation failed.
+
+ \sa bind(), listen()
+*/
+
+/*!
+ \fn qint64 Q3SocketDevice::bytesAvailable() const
+
+ Returns the number of bytes available for reading, or -1 if an
+ error occurred.
+
+ \warning On Microsoft Windows, we use the ioctlsocket() function
+ to determine the number of bytes queued on the socket. According
+ to Microsoft (KB Q125486), ioctlsocket() sometimes returns an
+ incorrect number. The only safe way to determine the amount of
+ data on the socket is to read it using readBlock(). QSocket has
+ workarounds to deal with this problem.
+*/
+
+/*!
+ \fn Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const
+
+ Wait up to \a msecs milliseconds for more data to be available. If
+ \a msecs is -1 the call will block indefinitely.
+
+ Returns the number of bytes available for reading, or -1 if an
+ error occurred.
+
+ If \a timeout is non-null and no error occurred (i.e. it does not
+ return -1): this function sets *\a timeout to true, if the reason
+ for returning was that the timeout was reached; otherwise it sets
+ *\a timeout to false. This is useful to find out if the peer
+ closed the connection.
+
+ \warning This is a blocking call and should be avoided in event
+ driven applications.
+
+ \sa bytesAvailable()
+*/
+
+/*!
+ \fn qint64 Q3SocketDevice::writeData( const char *data, qint64 len )
+
+ Writes \a len bytes to the socket from \a data and returns the
+ number of bytes written. Returns -1 if an error occurred.
+
+ This is used for Q3SocketDevice::Stream sockets.
+*/
+
+/*!
+ \fn void Q3SocketDevice::fetchConnectionParameters()
+
+ Fetches information about both ends of the connection: whatever is
+ available.
+*/
+
+/*!
+ \fn Q_UINT16 Q3SocketDevice::peerPort() const
+
+ Returns the port number of the port this socket device is
+ connected to. This may be 0 for a while, but is set to something
+ sensible as soon as a sensible value is available.
+
+ Note that for Datagram sockets, this is the source port of the
+ last packet received, and that it is in native byte order.
+*/
+
+/*!
+ \fn QHostAddress Q3SocketDevice::peerAddress() const
+
+ Returns the address of the port this socket device is connected
+ to. This may be 0.0.0.0 for a while, but is set to something
+ sensible as soon as a sensible value is available.
+
+ Note that for Datagram sockets, this is the source port of the
+ last packet received.
+*/
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3socketdevice.h b/src/qt3support/network/q3socketdevice.h
new file mode 100644
index 0000000000..fde591d53a
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice.h
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SOCKETDEVICE_H
+#define Q3SOCKETDEVICE_H
+
+#include <QtCore/qiodevice.h>
+#include <QtNetwork/qhostaddress.h> // int->QHostAddress conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3Support)
+
+#ifndef QT_NO_NETWORK
+
+class Q3SocketDevicePrivate;
+
+class Q_COMPAT_EXPORT Q3SocketDevice: public QIODevice
+{
+public:
+ enum Type { Stream, Datagram };
+ enum Protocol { IPv4, IPv6, Unknown };
+
+ Q3SocketDevice( Type type = Stream );
+ Q3SocketDevice( Type type, Protocol protocol, int dummy );
+ Q3SocketDevice( int socket, Type type );
+ virtual ~Q3SocketDevice();
+
+ bool isValid() const;
+ Type type() const;
+ Protocol protocol() const;
+
+ int socket() const;
+ virtual void setSocket( int socket, Type type );
+
+ bool open( OpenMode mode );
+ bool open( int mode ) { return open((OpenMode)mode); }
+ void close();
+ bool flush();
+
+ // Implementation of QIODevice abstract virtual functions
+ Offset size() const;
+ Offset at() const;
+ bool at( Offset );
+ bool atEnd() const;
+
+ bool blocking() const;
+ virtual void setBlocking( bool );
+
+ bool addressReusable() const;
+ virtual void setAddressReusable( bool );
+
+ int receiveBufferSize() const;
+ virtual void setReceiveBufferSize( uint );
+ int sendBufferSize() const;
+ virtual void setSendBufferSize( uint );
+
+ virtual bool connect( const QHostAddress &, Q_UINT16 );
+
+ virtual bool bind( const QHostAddress &, Q_UINT16 );
+ virtual bool listen( int backlog );
+ virtual int accept();
+
+ qint64 bytesAvailable() const;
+ Q_LONG waitForMore( int msecs, bool *timeout=0 ) const;
+ virtual Q_LONG writeBlock( const char *data, Q_ULONG len,
+ const QHostAddress & host, Q_UINT16 port );
+ inline Q_LONG writeBlock(const char *data, Q_ULONG len)
+ { return qint64(write(data, qint64(len))); }
+ inline qint64 readBlock(char *data, Q_ULONG maxlen)
+ { return qint64(read(data, qint64(maxlen))); }
+
+ Q_UINT16 port() const;
+ Q_UINT16 peerPort() const;
+ QHostAddress address() const;
+ QHostAddress peerAddress() const;
+
+ enum Error {
+ NoError,
+ AlreadyBound,
+ Inaccessible,
+ NoResources,
+ InternalError,
+ Bug = InternalError, // ### remove in 4.0?
+ Impossible,
+ NoFiles,
+ ConnectionRefused,
+ NetworkFailure,
+ UnknownError
+ };
+ Error error() const;
+
+ inline bool isSequential() const { return true; }
+
+protected:
+ void setError( Error err );
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+
+private:
+ int fd;
+ Type t;
+ Q_UINT16 p;
+ QHostAddress a;
+ Q_UINT16 pp;
+ QHostAddress pa;
+ Q3SocketDevice::Error e;
+ Q3SocketDevicePrivate * d;
+
+ enum Option { Broadcast, ReceiveBuffer, ReuseAddress, SendBuffer };
+
+ int option( Option ) const;
+ virtual void setOption( Option, int );
+
+ void fetchConnectionParameters();
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ void fetchPeerConnectionParameters();
+#endif
+
+ static void init();
+ int createNewSocket();
+ Protocol getProtocol() const;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3SocketDevice( const Q3SocketDevice & );
+ Q3SocketDevice &operator=( const Q3SocketDevice & );
+#endif
+};
+
+#endif // QT_NO_NETWORK
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SOCKETDEVICE_H
diff --git a/src/qt3support/network/q3socketdevice_unix.cpp b/src/qt3support/network/q3socketdevice_unix.cpp
new file mode 100644
index 0000000000..e83a789ac1
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice_unix.cpp
@@ -0,0 +1,926 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+// Almost always the same. If not, specify in qplatformdefs.h.
+#if !defined(QT_SOCKOPTLEN_T)
+# define QT_SOCKOPTLEN_T QT_SOCKLEN_T
+#endif
+
+// Tru64 redefines accept -> _accept with _XOPEN_SOURCE_EXTENDED
+static inline int qt_socket_accept(int s, struct sockaddr *addr, QT_SOCKLEN_T *addrlen)
+{ return ::accept(s, addr, addrlen); }
+#if defined(accept)
+# undef accept
+#endif
+
+// UnixWare 7 redefines listen -> _listen
+static inline int qt_socket_listen(int s, int backlog)
+{ return ::listen(s, backlog); }
+#if defined(listen)
+# undef listen
+#endif
+
+// UnixWare 7 redefines socket -> _socket
+static inline int qt_socket_socket(int domain, int type, int protocol)
+{ return ::socket(domain, type, protocol); }
+#if defined(socket)
+# undef socket
+#endif
+
+#include "q3socketdevice.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qwindowdefs.h"
+
+#include <errno.h>
+#include <sys/types.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline void qt_socket_getportaddr( struct sockaddr *sa,
+ Q_UINT16 *port, QHostAddress *addr )
+{
+#if !defined(QT_NO_IPV6)
+ if ( sa->sa_family == AF_INET6 ) {
+ struct sockaddr_in6 *sa6 = ( struct sockaddr_in6 * )sa;
+ Q_IPV6ADDR tmp;
+ memcpy( &tmp, &sa6->sin6_addr.s6_addr, sizeof(tmp) );
+ QHostAddress a( tmp );
+ *addr = a;
+ *port = ntohs( sa6->sin6_port );
+ return;
+ }
+#endif
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ QHostAddress a( ntohl( sa4->sin_addr.s_addr ) );
+ *port = ntohs( sa4->sin_port );
+ *addr = QHostAddress( ntohl( sa4->sin_addr.s_addr ) );
+ return;
+}
+
+
+//#define QSOCKETDEVICE_DEBUG
+
+// internal
+void Q3SocketDevice::init()
+{
+}
+
+
+Q3SocketDevice::Protocol Q3SocketDevice::getProtocol() const
+{
+ if ( isValid() ) {
+#if !defined (QT_NO_IPV6)
+ struct sockaddr_storage sa;
+#else
+ struct sockaddr sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ QT_SOCKLEN_T sz = sizeof( sa );
+#if !defined (QT_NO_IPV6)
+ struct sockaddr *sap = reinterpret_cast<struct sockaddr *>(&sa);
+ if ( !::getsockname(fd, sap, &sz) ) {
+ switch ( sap->sa_family ) {
+ case AF_INET:
+ return IPv4;
+ case AF_INET6:
+ return IPv6;
+ default:
+ return Unknown;
+ }
+ }
+#else
+ if ( !::getsockname(fd, &sa, &sz) ) {
+ switch ( sa.sa_family ) {
+ case AF_INET:
+ return IPv4;
+ default:
+ return Unknown;
+ }
+ }
+#endif
+ }
+ return Unknown;
+}
+
+
+int Q3SocketDevice::createNewSocket()
+{
+#if !defined(QT_NO_IPV6)
+ int s = qt_socket_socket( protocol() == IPv6 ? AF_INET6 : AF_INET,
+ t == Datagram ? SOCK_DGRAM : SOCK_STREAM, 0 );
+#else
+ int s = qt_socket_socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+#endif
+ if ( s < 0 ) {
+ switch( errno ) {
+ case EPROTONOSUPPORT:
+ e = InternalError; // 0 is supposed to work for both types
+ break;
+ case ENFILE:
+ e = NoFiles; // special case for this
+ break;
+ case EACCES:
+ e = Inaccessible;
+ break;
+ case ENOBUFS:
+ case ENOMEM:
+ e = NoResources;
+ break;
+ case EINVAL:
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ } else {
+ return s;
+ }
+ return -1;
+}
+
+void Q3SocketDevice::close()
+{
+ if ( fd == -1 || !isOpen() ) // already closed
+ return;
+ resetStatus();
+ setOpenMode(NotOpen);
+ ::close( fd );
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::close: Closed socket %x", fd );
+#endif
+ fd = -1;
+ fetchConnectionParameters();
+ QIODevice::close();
+}
+
+
+bool Q3SocketDevice::blocking() const
+{
+ if ( !isValid() )
+ return true;
+ int s = fcntl(fd, F_GETFL, 0);
+ return !(s >= 0 && ((s & O_NDELAY) != 0));
+}
+
+
+void Q3SocketDevice::setBlocking( bool enable )
+{
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::setBlocking( %d )", enable );
+#endif
+ if ( !isValid() )
+ return;
+ int tmp = ::fcntl(fd, F_GETFL, 0);
+ if ( tmp >= 0 )
+ tmp = ::fcntl( fd, F_SETFL, enable ? (tmp&~O_NDELAY) : (tmp|O_NDELAY) );
+ if ( tmp >= 0 )
+ return;
+ if ( e )
+ return;
+ switch( errno ) {
+ case EACCES:
+ case EBADF:
+ e = Impossible;
+ break;
+ case EFAULT:
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EDEADLK:
+ case EINTR:
+ case EINVAL:
+ case EMFILE:
+ case ENOLCK:
+ case EPERM:
+ default:
+ e = UnknownError;
+ }
+}
+
+
+int Q3SocketDevice::option( Option opt ) const
+{
+ if ( !isValid() )
+ return -1;
+ int n = -1;
+ int v = -1;
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ }
+ if ( n != -1 ) {
+ QT_SOCKOPTLEN_T len;
+ len = sizeof(v);
+ int r = ::getsockopt( fd, SOL_SOCKET, n, (char*)&v, &len );
+ if ( r >= 0 )
+ return v;
+ if ( !e ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable function
+ switch( errno ) {
+ case EBADF:
+ case ENOTSOCK:
+ that->e = Impossible;
+ break;
+ case EFAULT:
+ that->e = InternalError;
+ break;
+ default:
+ that->e = UnknownError;
+ break;
+ }
+ }
+ return -1;
+ }
+ return v;
+}
+
+
+void Q3SocketDevice::setOption( Option opt, int v )
+{
+ if ( !isValid() )
+ return;
+ int n = -1; // for really, really bad compilers
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ default:
+ return;
+ }
+ if ( ::setsockopt( fd, SOL_SOCKET, n, (char*)&v, sizeof(v)) < 0 &&
+ e == NoError ) {
+ switch( errno ) {
+ case EBADF:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+ case EFAULT:
+ e = InternalError;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+}
+
+
+bool Q3SocketDevice::connect( const QHostAddress &addr, Q_UINT16 port )
+{
+ if ( !isValid() )
+ return false;
+
+ pa = addr;
+ pp = port;
+
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ QT_SOCKLEN_T aalen;
+
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 a6;
+
+ if ( addr.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR ip6 = addr.toIPv6Address();
+ memcpy( &a6.sin6_addr.s6_addr, &ip6, sizeof(ip6) );
+
+ aalen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( addr.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( addr.toIPv4Address() );
+
+ aalen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ int r = QT_SOCKET_CONNECT( fd, aa, aalen );
+ if ( r == 0 ) {
+ fetchConnectionParameters();
+ return true;
+ }
+ if ( errno == EISCONN || errno == EALREADY || errno == EINPROGRESS ) {
+ fetchConnectionParameters();
+ return true;
+ }
+ if ( e != NoError || errno == EAGAIN || errno == EWOULDBLOCK ) {
+ return false;
+ }
+ switch( errno ) {
+ case EBADF:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+ case EFAULT:
+ case EAFNOSUPPORT:
+ e = InternalError;
+ break;
+ case ECONNREFUSED:
+ e = ConnectionRefused;
+ break;
+ case ETIMEDOUT:
+ case ENETUNREACH:
+ e = NetworkFailure;
+ break;
+ case EADDRINUSE:
+ e = NoResources;
+ break;
+ case EACCES:
+ case EPERM:
+ e = Inaccessible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+}
+
+
+bool Q3SocketDevice::bind( const QHostAddress &address, Q_UINT16 port )
+{
+ if ( !isValid() )
+ return false;
+ int r;
+ struct sockaddr_in a4;
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 a6;
+
+ if ( address.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR tmp = address.toIPv6Address();
+ memcpy( &a6.sin6_addr.s6_addr, &tmp, sizeof(tmp) );
+
+ r = QT_SOCKET_BIND( fd, (struct sockaddr *)&a6, sizeof(a6) );
+ } else
+#endif
+ if ( address.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( address.toIPv4Address() );
+
+ r = QT_SOCKET_BIND( fd, (struct sockaddr*)&a4, sizeof(a4) );
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ if ( r < 0 ) {
+ switch( errno ) {
+ case EINVAL:
+ e = AlreadyBound;
+ break;
+ case EACCES:
+ e = Inaccessible;
+ break;
+ case ENOMEM:
+ e = NoResources;
+ break;
+ case EFAULT: // a was illegal
+ case ENAMETOOLONG: // sz was wrong
+ e = InternalError;
+ break;
+ case EBADF: // AF_UNIX only
+ case ENOTSOCK: // AF_UNIX only
+ case EROFS: // AF_UNIX only
+ case ENOENT: // AF_UNIX only
+ case ENOTDIR: // AF_UNIX only
+ case ELOOP: // AF_UNIX only
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+ }
+ fetchConnectionParameters();
+ return true;
+}
+
+
+bool Q3SocketDevice::listen( int backlog )
+{
+ if ( !isValid() )
+ return false;
+ if ( qt_socket_listen( fd, backlog ) >= 0 )
+ return true;
+ if ( !e )
+ e = Impossible;
+ return false;
+}
+
+
+int Q3SocketDevice::accept()
+{
+ if ( !isValid() )
+ return -1;
+
+#if !defined (QT_NO_IPV6)
+ struct sockaddr_storage aa;
+#else
+ struct sockaddr aa;
+#endif
+ QT_SOCKLEN_T l = sizeof( aa );
+ bool done;
+ int s;
+ do {
+ s = qt_socket_accept( fd, (struct sockaddr*)&aa, &l );
+ // we'll blithely throw away the stuff accept() wrote to aa
+ done = true;
+ if ( s < 0 && e == NoError ) {
+ switch( errno ) {
+ case EINTR:
+ done = false;
+ break;
+#if defined(EPROTO)
+ case EPROTO:
+#endif
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case ENOPROTOOPT:
+ case EHOSTDOWN:
+ case EOPNOTSUPP:
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ // in all these cases, an error happened during connection
+ // setup. we're not interested in what happened, so we
+ // just treat it like the client-closed-quickly case.
+ case EPERM:
+ // firewalling wouldn't let us accept. we treat it like
+ // the client-closed-quickly case.
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ // the client closed the connection before we got around
+ // to accept()ing it.
+ break;
+ case EBADF:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+ case EFAULT:
+ e = InternalError;
+ break;
+ case ENOMEM:
+ case ENOBUFS:
+ e = NoResources;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ } while (!done);
+ return s;
+}
+
+
+qint64 Q3SocketDevice::bytesAvailable() const
+{
+ if ( !isValid() )
+ return -1;
+
+ /*
+ Apparently, there is not consistency among different operating
+ systems on how to use FIONREAD.
+
+ FreeBSD, Linux and Solaris all expect the 3rd argument to
+ ioctl() to be an int, which is normally 32-bit even on 64-bit
+ machines.
+
+ IRIX, on the other hand, expects a size_t, which is 64-bit on
+ 64-bit machines.
+
+ So, the solution is to use size_t initialized to zero to make
+ sure all bits are set to zero, preventing underflow with the
+ FreeBSD/Linux/Solaris ioctls.
+ */
+ size_t nbytes = 0;
+ // gives shorter than true amounts on Unix domain sockets.
+ if ( ::ioctl(fd, FIONREAD, (char*)&nbytes) < 0 )
+ return -1;
+ return (Q_LONG) *((int *) &nbytes) + QIODevice::bytesAvailable();
+}
+
+
+Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( !isValid() )
+ return -1;
+ if ( fd >= FD_SETSIZE )
+ return -1;
+
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO( &fds );
+ FD_SET( fd, &fds );
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ int rv = select( fd+1, &fds, 0, 0, msecs < 0 ? 0 : &tv );
+
+ if ( rv < 0 )
+ return -1;
+
+ if ( timeout ) {
+ if ( rv == 0 )
+ *timeout = true;
+ else
+ *timeout = false;
+ }
+
+ return bytesAvailable();
+}
+
+
+qint64 Q3SocketDevice::readData( char *data, qint64 maxlen )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && maxlen != 0 ) {
+ qWarning( "Q3SocketDevice::readBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isValid() ) {
+ qWarning( "Q3SocketDevice::readBlock: Invalid socket" );
+ return -1;
+ }
+ if ( !isOpen() ) {
+ qWarning( "Q3SocketDevice::readBlock: Device is not open" );
+ return -1;
+ }
+ if ( !isReadable() ) {
+ qWarning( "Q3SocketDevice::readBlock: Read operation not permitted" );
+ return -1;
+ }
+#endif
+ bool done = false;
+ int r = 0;
+ while ( done == false ) {
+ if ( t == Datagram ) {
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_storage aa;
+#else
+ struct sockaddr_in aa;
+#endif
+ memset( &aa, 0, sizeof(aa) );
+ QT_SOCKLEN_T sz;
+ sz = sizeof( aa );
+ r = ::recvfrom( fd, data, maxlen, 0,
+ (struct sockaddr *)&aa, &sz );
+
+ qt_socket_getportaddr( (struct sockaddr *)&aa, &pp, &pa);
+
+ } else {
+ r = ::read( fd, data, maxlen );
+ }
+ done = true;
+ if ( r == 0 && t == Stream && maxlen > 0 ) {
+ // connection closed
+ close();
+ } else if ( r >= 0 || errno == EAGAIN || errno == EWOULDBLOCK ) {
+ // nothing
+ } else if ( errno == EINTR ) {
+ done = false;
+ } else if ( e == NoError ) {
+ switch( errno ) {
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ case ENOTCONN:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ case EPIPE:
+ case ECONNRESET:
+ // connection closed
+ close();
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+qint64 Q3SocketDevice::writeData( const char *data, qint64 len )
+{
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ bool done = false;
+ int r = 0;
+ bool timeout;
+ while ( !done ) {
+ r = ::write( fd, data, len );
+ done = true;
+ if ( r < 0 && e == NoError &&
+ errno != EAGAIN && errno != EWOULDBLOCK ) {
+ switch( errno ) {
+ case EINTR: // signal - call read() or whatever again
+ done = false;
+ break;
+ case EPIPE:
+ case ECONNRESET:
+ // connection closed
+ close();
+ r = 0;
+ break;
+ case ENOSPC:
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ case ENOTCONN:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ } else if ( waitForMore( 0, &timeout ) == 0 ) {
+ if ( !timeout ) {
+ // connection closed
+ close();
+ }
+ }
+ }
+ return r;
+}
+
+
+Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const QHostAddress & host, Q_UINT16 port )
+{
+ if ( t != Datagram ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Not datagram" );
+#endif
+ return -1; // for now - later we can do t/tcp
+ }
+
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ QT_SOCKLEN_T slen;
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_in6 a6;
+ if ( host.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+
+ Q_IPV6ADDR tmp = host.toIPv6Address();
+ memcpy( &a6.sin6_addr.s6_addr, &tmp, sizeof(tmp) );
+ slen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( host.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( host.toIPv4Address() );
+ slen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return -1;
+ }
+
+ // we'd use MSG_DONTWAIT + MSG_NOSIGNAL if Stevens were right.
+ // but apparently Stevens and most implementors disagree
+ bool done = false;
+ int r = 0;
+ while ( !done ) {
+ r = ::sendto( fd, data, len, 0, aa, slen);
+ done = true;
+ if ( r < 0 && e == NoError &&
+ errno != EAGAIN && errno != EWOULDBLOCK ) {
+ switch( errno ) {
+ case EINTR: // signal - call read() or whatever again
+ done = false;
+ break;
+ case ENOSPC:
+ case EPIPE:
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ case ENOTCONN:
+ case ENOTSOCK:
+ e = Impossible;
+ break;
+#if defined(ENONET)
+ case ENONET:
+#endif
+ case EHOSTUNREACH:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+void Q3SocketDevice::fetchConnectionParameters()
+{
+ if ( !isValid() ) {
+ p = 0;
+ a = QHostAddress();
+ pp = 0;
+ pa = QHostAddress();
+ return;
+ }
+#if !defined(QT_NO_IPV6)
+ struct sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ QT_SOCKLEN_T sz;
+ sz = sizeof( sa );
+ if ( !::getsockname( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)&sa, &p, &a );
+
+ sz = sizeof( sa );
+ if ( !::getpeername( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)&sa, &pp, &pa );
+}
+
+
+Q_UINT16 Q3SocketDevice::peerPort() const
+{
+ return pp;
+}
+
+
+QHostAddress Q3SocketDevice::peerAddress() const
+{
+ return pa;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_NETWORK
diff --git a/src/qt3support/network/q3socketdevice_win.cpp b/src/qt3support/network/q3socketdevice_win.cpp
new file mode 100644
index 0000000000..f2d034be25
--- /dev/null
+++ b/src/qt3support/network/q3socketdevice_win.cpp
@@ -0,0 +1,1068 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3socketdevice.h"
+#include "qwindowdefs.h"
+#include "qdatetime.h"
+
+#include <qcoreapplication.h>
+
+#include <string.h>
+
+#if defined (QT_NO_IPV6)
+# include <windows.h>
+# include <winsock.h>
+#else
+# if defined (Q_CC_BOR) || defined (Q_CC_GNU)
+# include <winsock2.h>
+# elif defined (Q_CC_INTEL)
+# include <winsock.h>
+# else
+# include <windows.h>
+# if defined(Q_OS_WINCE)
+# include <winsock.h>
+# endif
+# endif
+// Use our own defines and structs which we know are correct
+# define QT_SS_MAXSIZE 128
+# define QT_SS_ALIGNSIZE (sizeof(__int64))
+# define QT_SS_PAD1SIZE (QT_SS_ALIGNSIZE - sizeof (short))
+# define QT_SS_PAD2SIZE (QT_SS_MAXSIZE - (sizeof (short) + QT_SS_PAD1SIZE + QT_SS_ALIGNSIZE))
+
+QT_BEGIN_NAMESPACE
+
+struct qt_sockaddr_storage {
+ short ss_family;
+ char __ss_pad1[QT_SS_PAD1SIZE];
+ __int64 __ss_align;
+ char __ss_pad2[QT_SS_PAD2SIZE];
+};
+
+// sockaddr_in6 size changed between old and new SDK
+// Only the new version is the correct one, so always
+// use this structure.
+struct qt_in6_addr {
+ u_char qt_s6_addr[16];
+};
+typedef struct {
+ short sin6_family; /* AF_INET6 */
+ u_short sin6_port; /* Transport level port number */
+ u_long sin6_flowinfo; /* IPv6 flow information */
+ struct qt_in6_addr sin6_addr; /* IPv6 address */
+ u_long sin6_scope_id; /* set of interfaces for a scope */
+} qt_sockaddr_in6;
+#endif
+
+#ifndef AF_INET6
+#define AF_INET6 23 /* Internetwork Version 6 */
+#endif
+
+#ifndef NO_ERRNO_H
+QT_BEGIN_INCLUDE_NAMESPACE
+# if defined(Q_OS_WINCE)
+# include "qfunctions_wince.h"
+# else
+# include <errno.h>
+# endif
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+
+#if defined(SOCKLEN_T)
+#undef SOCKLEN_T
+#endif
+
+#define SOCKLEN_T int // #### Winsock 1.1
+
+static int initialized = 0x00; // Holds the Winsock version
+
+static void cleanupWinSock() // post-routine
+{
+ WSACleanup();
+ initialized = 0x00;
+}
+
+static inline void qt_socket_getportaddr( struct sockaddr *sa,
+ quint16 *port, QHostAddress *addr )
+{
+#if !defined (QT_NO_IPV6)
+ if (sa->sa_family == AF_INET6) {
+ qt_sockaddr_in6 *sa6 = (qt_sockaddr_in6 *)sa;
+ Q_IPV6ADDR tmp;
+ for ( int i = 0; i < 16; ++i )
+ tmp.c[i] = sa6->sin6_addr.qt_s6_addr[i];
+ QHostAddress a( tmp );
+ *addr = a;
+ *port = ntohs( sa6->sin6_port );
+ return;
+ }
+#endif
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ QHostAddress a( ntohl( sa4->sin_addr.s_addr ) );
+ *port = ntohs( sa4->sin_port );
+ *addr = a;
+}
+
+void Q3SocketDevice::init()
+{
+#if !defined(QT_NO_IPV6)
+ if ( !initialized ) {
+ WSAData wsadata;
+ // IPv6 requires Winsock v2.0 or better.
+ if ( WSAStartup( MAKEWORD(2,0), &wsadata ) != 0 ) {
+# if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: WinSock v2.0 initialization failed, disabling IPv6 support." );
+# endif
+ } else {
+ qAddPostRoutine( cleanupWinSock );
+ initialized = 0x20;
+ return;
+ }
+ }
+#endif
+
+ if (!initialized) {
+ WSAData wsadata;
+ if ( WSAStartup( MAKEWORD(1,1), &wsadata ) != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "Q3SocketDevice: WinSock initialization failed" );
+#endif
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice: WinSock initialization failed" );
+#endif
+ return;
+ }
+ qAddPostRoutine( cleanupWinSock );
+ initialized = 0x11;
+ }
+}
+
+Q3SocketDevice::Protocol Q3SocketDevice::getProtocol() const
+{
+ if ( isValid() ) {
+#if !defined (QT_NO_IPV6)
+ struct qt_sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ SOCKLEN_T sz = sizeof( sa );
+ if ( !::getsockname(fd, (struct sockaddr *)&sa, &sz) ) {
+#if !defined (QT_NO_IPV6)
+ switch ( sa.ss_family ) {
+ case AF_INET:
+ return IPv4;
+ case AF_INET6:
+ return IPv6;
+ default:
+ return Unknown;
+ }
+#else
+ switch ( sa.sin_family ) {
+ case AF_INET:
+ return IPv4;
+ default:
+ return Unknown;
+ }
+#endif
+ }
+ }
+ return Unknown;
+}
+
+int Q3SocketDevice::createNewSocket( )
+{
+#if !defined(QT_NO_IPV6)
+ SOCKET s;
+ // Support IPv6 for Winsock v2.0++
+ if ( initialized >= 0x20 && protocol() == IPv6 ) {
+ s = ::socket( AF_INET6, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+ } else {
+ s = ::socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+ }
+#else
+ SOCKET s = ::socket( AF_INET, t==Datagram?SOCK_DGRAM:SOCK_STREAM, 0 );
+#endif
+ if ( s == INVALID_SOCKET ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ // ### what to use here?
+ e = NetworkFailure;
+ //e = Inaccessible;
+ break;
+ case WSAEMFILE:
+ e = NoFiles; // special case for this
+ break;
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEAFNOSUPPORT:
+ case WSAEPROTOTYPE:
+ case WSAEPROTONOSUPPORT:
+ case WSAESOCKTNOSUPPORT:
+ e = InternalError;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ } else {
+ return s;
+ }
+ return -1;
+}
+
+
+void Q3SocketDevice::close()
+{
+ if ( fd == -1 || !isOpen() ) // already closed
+ return;
+ resetStatus();
+ setOpenMode(NotOpen);
+ ::closesocket( fd );
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::close: Closed socket %x", fd );
+#endif
+ fd = -1;
+ fetchConnectionParameters();
+ QIODevice::close();
+}
+
+
+bool Q3SocketDevice::blocking() const
+{
+ return true;
+}
+
+
+void Q3SocketDevice::setBlocking( bool enable )
+{
+#if defined(QSOCKETDEVICE_DEBUG)
+ qDebug( "Q3SocketDevice::setBlocking( %d )", enable );
+#endif
+ if ( !isValid() )
+ return;
+
+ unsigned long dummy = enable ? 0 : 1;
+ ioctlsocket( fd, FIONBIO, &dummy );
+}
+
+
+int Q3SocketDevice::option( Option opt ) const
+{
+ if ( !isValid() )
+ return -1;
+ int n = -1;
+ int v = -1;
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ }
+ if ( n != -1 ) {
+ SOCKLEN_T len = sizeof(v);
+ int r = ::getsockopt( fd, SOL_SOCKET, n, (char*)&v, &len );
+ if ( r != SOCKET_ERROR )
+ return v;
+ if ( !e ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable function
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ that->e = Impossible;
+ break;
+ case WSAENETDOWN:
+ that->e = NetworkFailure;
+ break;
+ case WSAEFAULT:
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ that->e = InternalError;
+ break;
+ case WSAEINPROGRESS:
+ that->e = NoResources;
+ break;
+ case WSAENOTSOCK:
+ that->e = Impossible;
+ break;
+ default:
+ that->e = UnknownError;
+ break;
+ }
+ }
+ return -1;
+ }
+ return v;
+}
+
+
+void Q3SocketDevice::setOption( Option opt, int v )
+{
+ if ( !isValid() )
+ return;
+ int n = -1; // for really, really bad compilers
+ switch ( opt ) {
+ case Broadcast:
+ n = SO_BROADCAST;
+ break;
+ case ReceiveBuffer:
+ n = SO_RCVBUF;
+ break;
+ case ReuseAddress:
+ n = SO_REUSEADDR;
+ break;
+ case SendBuffer:
+ n = SO_SNDBUF;
+ break;
+ default:
+ return;
+ }
+ int r = ::setsockopt( fd, SOL_SOCKET, n, (char*)&v, sizeof(v) );
+ if ( r == SOCKET_ERROR && e == NoError ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ e = NetworkFailure;
+ break;
+ case WSAEFAULT:
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ e = InternalError;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ break;
+ case WSAENETRESET:
+ case WSAENOTCONN:
+ e = Impossible; // ### ?
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+}
+
+
+bool Q3SocketDevice::connect( const QHostAddress &addr, quint16 port )
+{
+ if ( !isValid() )
+ return false;
+
+ pa = addr;
+ pp = port;
+
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ SOCKLEN_T aalen;
+
+#if !defined(QT_NO_IPV6)
+ qt_sockaddr_in6 a6;
+
+ if ( initialized >= 0x20 && addr.isIPv6Address() ) {
+ memset(&a6, 0, sizeof(a6));
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR ip6 = addr.toIPv6Address();
+ memcpy( &a6.sin6_addr.qt_s6_addr, &ip6, sizeof(ip6) );
+
+ aalen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( addr.isIPv4Address() ) {
+ memset(&a4, 0, sizeof(a4));
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons(port);
+ a4.sin_addr.s_addr = htonl(addr.toIPv4Address());
+
+ aalen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ int r = ::connect( fd, aa, aalen );
+
+ if ( r == SOCKET_ERROR )
+ {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ e = NetworkFailure;
+ break;
+ case WSAEADDRINUSE:
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEINTR:
+ e = UnknownError; // ### ?
+ break;
+ case WSAEALREADY:
+ // ### ?
+ break;
+ case WSAEADDRNOTAVAIL:
+ e = ConnectionRefused; // ### ?
+ break;
+ case WSAEAFNOSUPPORT:
+ case WSAEFAULT:
+ e = InternalError;
+ break;
+ case WSAEINVAL:
+ break;
+ case WSAECONNREFUSED:
+ e = ConnectionRefused;
+ break;
+ case WSAEISCONN:
+ goto successful;
+ case WSAENETUNREACH:
+ case WSAETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ break;
+ case WSAEACCES:
+ e = Inaccessible;
+ break;
+ case 10107:
+ // Workaround for a problem with the WinSock Proxy Server. See
+ // also support/arc-12/25557 for details on the problem.
+ goto successful;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+ }
+successful:
+ fetchConnectionParameters();
+ return true;
+}
+
+
+bool Q3SocketDevice::bind( const QHostAddress &address, quint16 port )
+{
+ if ( !isValid() )
+ return false;
+ int r;
+ struct sockaddr_in a4;
+#if !defined(QT_NO_IPV6)
+ qt_sockaddr_in6 a6;
+
+ if ( initialized >= 0x20 && address.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+ Q_IPV6ADDR tmp = address.toIPv6Address();
+ memcpy( &a6.sin6_addr.qt_s6_addr, &tmp, sizeof(tmp) );
+
+ r = ::bind( fd, (struct sockaddr *)&a6, sizeof(struct qt_sockaddr_storage) );
+ } else
+#endif
+ if ( address.isIPv4Address() ) {
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( address.toIPv4Address() );
+
+ r = ::bind( fd, (struct sockaddr*)&a4, sizeof(struct sockaddr_in) );
+ } else {
+ e = Impossible;
+ return false;
+ }
+
+ if ( r == SOCKET_ERROR ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ e = NetworkFailure;
+ break;
+ case WSAEACCES:
+ e = Inaccessible;
+ break;
+ case WSAEADDRNOTAVAIL:
+ e = Inaccessible;
+ break;
+ case WSAEFAULT:
+ e = InternalError;
+ break;
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEADDRINUSE:
+ case WSAEINVAL:
+ e = AlreadyBound;
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ return false;
+ }
+ fetchConnectionParameters();
+ return true;
+}
+
+
+bool Q3SocketDevice::listen( int backlog )
+{
+ if ( !isValid() )
+ return false;
+ if ( ::listen( fd, backlog ) >= 0 )
+ return true;
+ if ( !e )
+ e = Impossible;
+ return false;
+}
+
+
+int Q3SocketDevice::accept()
+{
+ if ( !isValid() )
+ return -1;
+#if !defined(QT_NO_IPV6)
+ struct qt_sockaddr_storage a;
+#else
+ struct sockaddr a;
+#endif
+ SOCKLEN_T l = sizeof(a);
+ bool done;
+ SOCKET s;
+ do {
+ s = ::accept( fd, (struct sockaddr*)&a, &l );
+ // we'll blithely throw away the stuff accept() wrote to a
+ done = true;
+ if ( s == INVALID_SOCKET && e == NoError ) {
+ switch( WSAGetLastError() ) {
+ case WSAEINTR:
+ done = false;
+ break;
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ case WSAEOPNOTSUPP:
+ // in all these cases, an error happened during connection
+ // setup. we're not interested in what happened, so we
+ // just treat it like the client-closed-quickly case.
+ break;
+ case WSAEFAULT:
+ e = InternalError;
+ break;
+ case WSAEMFILE:
+ case WSAEINPROGRESS:
+ case WSAENOBUFS:
+ e = NoResources;
+ break;
+ case WSAEINVAL:
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ } while (!done);
+ return s;
+}
+
+
+qint64 Q3SocketDevice::bytesAvailable() const
+{
+ if ( !isValid() )
+ return -1;
+ u_long nbytes = 0;
+ if ( ::ioctlsocket(fd, FIONREAD, &nbytes) < 0 )
+ return -1;
+
+ // ioctlsocket sometimes reports 1 byte available for datagrams
+ // while the following recvfrom returns -1 and claims connection
+ // was reset (udp is connectionless). so we peek one byte to
+ // catch this case and return 0 bytes available if recvfrom
+ // fails.
+ if (nbytes == 1 && t == Datagram) {
+ char c;
+ if (::recvfrom(fd, &c, sizeof(c), MSG_PEEK, 0, 0) == SOCKET_ERROR)
+ return 0;
+ }
+
+ return nbytes;
+}
+
+
+Q_LONG Q3SocketDevice::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( !isValid() )
+ return -1;
+
+ fd_set fds;
+ memset(&fds, 0, sizeof(fd_set));
+ fds.fd_count = 1;
+ fds.fd_array[0] = fd;
+
+ struct timeval tv;
+
+ tv.tv_sec = msecs / 1000;
+ tv.tv_usec = (msecs % 1000) * 1000;
+
+ int rv = select( fd+1, &fds, 0, 0, msecs < 0 ? 0 : &tv );
+
+ if ( rv < 0 )
+ return -1;
+
+ if ( timeout ) {
+ if ( rv == 0 )
+ *timeout = true;
+ else
+ *timeout = false;
+ }
+
+ return bytesAvailable();
+}
+
+
+qint64 Q3SocketDevice::readData( char *data, qint64 maxlen )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && maxlen != 0 ) {
+ qWarning( "Q3SocketDevice::readBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isValid() ) {
+ qWarning( "Q3SocketDevice::readBlock: Invalid socket" );
+ return -1;
+ }
+ if ( !isOpen() ) {
+ qWarning( "Q3SocketDevice::readBlock: Device is not open" );
+ return -1;
+ }
+ if ( !isReadable() ) {
+ qWarning( "Q3SocketDevice::readBlock: Read operation not permitted" );
+ return -1;
+ }
+#endif
+ qint64 r = 0;
+ if ( t == Datagram ) {
+#if !defined(QT_NO_IPV6)
+ // With IPv6 support, we must be prepared to receive both IPv4
+ // and IPv6 packets. The generic SOCKADDR_STORAGE (struct
+ // sockaddr_storage on unix) replaces struct sockaddr.
+ struct qt_sockaddr_storage a;
+#else
+ struct sockaddr_in a;
+#endif
+ memset( &a, 0, sizeof(a) );
+ SOCKLEN_T sz;
+ sz = sizeof( a );
+ r = ::recvfrom( fd, data, maxlen, 0, (struct sockaddr *)&a, &sz );
+ qt_socket_getportaddr( (struct sockaddr *)(&a), &pp, &pa );
+ } else {
+ r = ::recv( fd, data, maxlen, 0 );
+ }
+ if ( r == 0 && t == Stream && maxlen > 0 ) {
+ if ( WSAGetLastError() != WSAEWOULDBLOCK ) {
+ // connection closed
+ close();
+ }
+ } else if ( r == SOCKET_ERROR && e == NoError ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAECONNABORTED:
+ close();
+ r = 0;
+ break;
+ case WSAETIMEDOUT:
+ case WSAECONNRESET:
+ /*
+ From msdn doc:
+ On a UDP datagram socket this error would indicate that a previous
+ send operation resulted in an ICMP "Port Unreachable" message.
+
+ So we should not close this socket just because one sendto failed.
+ */
+ if ( t != Datagram )
+ close(); // connection closed
+ r = 0;
+ break;
+ case WSAENETDOWN:
+ case WSAENETRESET:
+ e = NetworkFailure;
+ break;
+ case WSAEFAULT:
+ case WSAENOTCONN:
+ case WSAESHUTDOWN:
+ case WSAEINVAL:
+ e = Impossible;
+ break;
+ case WSAEINTR:
+ // ### ?
+ r = 0;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ break;
+ case WSAENOTSOCK:
+ e = Impossible;
+ break;
+ case WSAEOPNOTSUPP:
+ e = InternalError; // ### ?
+ break;
+ case WSAEWOULDBLOCK:
+ break;
+ case WSAEMSGSIZE:
+ e = NoResources; // ### ?
+ break;
+ case WSAEISCONN:
+ // ### ?
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ return r;
+}
+
+
+qint64 Q3SocketDevice::writeData( const char *data, qint64 len )
+{
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::writeBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ bool done = false;
+ qint64 r = 0;
+ while ( !done ) {
+ // Don't write more than 64K (see Knowledge Base Q201213).
+ r = ::send( fd, data, ( len>64*1024 ? 64*1024 : len ), 0 );
+ done = true;
+ if ( r == SOCKET_ERROR && e == NoError ) {//&& errno != WSAEAGAIN ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ case WSAEACCES:
+ case WSAENETRESET:
+ case WSAESHUTDOWN:
+ case WSAEHOSTUNREACH:
+ e = NetworkFailure;
+ break;
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ // connection closed
+ close();
+ r = 0;
+ break;
+ case WSAEINTR:
+ done = false;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ // ### perhaps try it later?
+ break;
+ case WSAEFAULT:
+ case WSAEOPNOTSUPP:
+ e = InternalError;
+ break;
+ case WSAENOBUFS:
+ // ### try later?
+ break;
+ case WSAEMSGSIZE:
+ e = NoResources;
+ break;
+ case WSAENOTCONN:
+ case WSAENOTSOCK:
+ case WSAEINVAL:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+Q_LONG Q3SocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const QHostAddress & host, quint16 port )
+{
+ if ( t != Datagram ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Not datagram" );
+#endif
+ return -1; // for now - later we can do t/tcp
+ }
+
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(QSOCKETDEVICE_DEBUG)
+ qWarning( "Q3SocketDevice::sendBlock: Write operation not permitted" );
+#endif
+ return -1;
+ }
+ struct sockaddr_in a4;
+ struct sockaddr *aa;
+ SOCKLEN_T slen;
+#if !defined(QT_NO_IPV6)
+ qt_sockaddr_in6 a6;
+ if ( initialized >= 0x20 && host.isIPv6Address() ) {
+ memset( &a6, 0, sizeof(a6) );
+ a6.sin6_family = AF_INET6;
+ a6.sin6_port = htons( port );
+
+ Q_IPV6ADDR tmp = host.toIPv6Address();
+ memcpy( &a6.sin6_addr.qt_s6_addr, &tmp, sizeof(tmp) );
+ slen = sizeof( a6 );
+ aa = (struct sockaddr *)&a6;
+ } else
+#endif
+ if ( host.isIPv4Address() ) {
+
+ memset( &a4, 0, sizeof(a4) );
+ a4.sin_family = AF_INET;
+ a4.sin_port = htons( port );
+ a4.sin_addr.s_addr = htonl( host.toIPv4Address() );
+ slen = sizeof(a4);
+ aa = (struct sockaddr *)&a4;
+ } else {
+ e = Impossible;
+ return -1;
+ }
+
+ // we'd use MSG_DONTWAIT + MSG_NOSIGNAL if Stevens were right.
+ // but apparently Stevens and most implementors disagree
+ bool done = false;
+ qint64 r = 0;
+ while ( !done ) {
+ r = ::sendto( fd, data, len, 0, aa, slen );
+ done = true;
+ if ( r == SOCKET_ERROR && e == NoError ) {//&& e != EAGAIN ) {
+ switch( WSAGetLastError() ) {
+ case WSANOTINITIALISED:
+ e = Impossible;
+ break;
+ case WSAENETDOWN:
+ case WSAEACCES:
+ case WSAENETRESET:
+ case WSAESHUTDOWN:
+ case WSAEHOSTUNREACH:
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ case WSAEADDRNOTAVAIL:
+ case WSAENETUNREACH:
+ case WSAETIMEDOUT:
+ e = NetworkFailure;
+ break;
+ case WSAEINTR:
+ done = false;
+ break;
+ case WSAEINPROGRESS:
+ e = NoResources;
+ // ### perhaps try it later?
+ break;
+ case WSAEFAULT:
+ case WSAEOPNOTSUPP:
+ case WSAEAFNOSUPPORT:
+ e = InternalError;
+ break;
+ case WSAENOBUFS:
+ case WSAEMSGSIZE:
+ e = NoResources;
+ break;
+ case WSAENOTCONN:
+ case WSAENOTSOCK:
+ case WSAEINVAL:
+ case WSAEDESTADDRREQ:
+ e = Impossible;
+ break;
+ case WSAEWOULDBLOCK:
+ r = 0;
+ break;
+ default:
+ e = UnknownError;
+ break;
+ }
+ }
+ }
+ return r;
+}
+
+
+void Q3SocketDevice::fetchConnectionParameters()
+{
+ if ( !isValid() ) {
+ p = 0;
+ a = QHostAddress();
+ pp = 0;
+ pa = QHostAddress();
+ return;
+ }
+#if !defined (QT_NO_IPV6)
+ struct qt_sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ SOCKLEN_T sz;
+ sz = sizeof( sa );
+ if ( !::getsockname( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)(&sa), &p, &a );
+ pp = 0;
+ pa = QHostAddress();
+}
+
+
+void Q3SocketDevice::fetchPeerConnectionParameters()
+{
+ // do the getpeername() lazy on Windows (sales/arc-18/37759 claims that
+ // there will be problems otherwise if you use MS Proxy server)
+#if !defined (QT_NO_IPV6)
+ struct qt_sockaddr_storage sa;
+#else
+ struct sockaddr_in sa;
+#endif
+ memset( &sa, 0, sizeof(sa) );
+ SOCKLEN_T sz;
+ sz = sizeof( sa );
+ if ( !::getpeername( fd, (struct sockaddr *)(&sa), &sz ) )
+ qt_socket_getportaddr( (struct sockaddr *)(&sa), &pp, &pa );
+}
+
+quint16 Q3SocketDevice::peerPort() const
+{
+ if ( pp==0 && isValid() ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable
+ that->fetchPeerConnectionParameters();
+ }
+ return pp;
+}
+
+
+QHostAddress Q3SocketDevice::peerAddress() const
+{
+ if ( pp==0 && isValid() ) {
+ Q3SocketDevice *that = (Q3SocketDevice*)this; // mutable
+ that->fetchPeerConnectionParameters();
+ }
+ return pa;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qt3support/network/q3url.cpp b/src/qt3support/network/q3url.cpp
new file mode 100644
index 0000000000..fc2fdb2fa2
--- /dev/null
+++ b/src/qt3support/network/q3url.cpp
@@ -0,0 +1,1319 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3url.h"
+
+#ifndef QT_NO_URL
+
+#include "q3cstring.h"
+#include "qdir.h"
+
+QT_BEGIN_NAMESPACE
+
+// used by q3filedialog.cpp
+bool qt_resolve_symlinks = true;
+
+class Q3UrlPrivate
+{
+public:
+ QString protocol;
+ QString user;
+ QString pass;
+ QString host;
+ QString path, cleanPath;
+ QString refEncoded;
+ QString queryEncoded;
+ bool isValid;
+ int port;
+ bool cleanPathDirty;
+};
+
+/*!
+ Replaces backslashes with slashes and removes multiple occurrences
+ of slashes or backslashes if \c allowMultiple is false.
+*/
+
+static void slashify( QString& s, bool allowMultiple = true )
+{
+ bool justHadSlash = false;
+ for ( int i = 0; i < (int)s.length(); i++ ) {
+ if ( !allowMultiple && justHadSlash &&
+ ( s[ i ] == QLatin1Char('/') || s[ i ] == QLatin1Char('\\') ) ) {
+ s.remove( i, 1 );
+ --i;
+ continue;
+ }
+ if ( s[ i ] == QLatin1Char('\\') )
+ s[ i ] = QLatin1Char('/');
+#if defined (Q_WS_MAC9)
+ if ( s[ i ] == QLatin1Char(':') && (i == (int)s.length()-1 || s[ i + 1 ] != QLatin1Char('/') ) ) //mac colon's go away, unless after a protocol
+ s[ i ] = QLatin1Char('/');
+#endif
+ if ( s[ i ] == QLatin1Char('/') )
+ justHadSlash = true;
+ else
+ justHadSlash = false;
+ }
+}
+
+
+
+/*!
+ \class Q3Url
+ \brief The Q3Url class provides a URL parser and simplifies working with URLs.
+
+ \compat
+
+ The Q3Url class is provided for simple work with URLs. It can
+ parse, decode, encode, etc.
+
+ Q3Url works with the decoded path and encoded query in turn.
+
+ Example:
+
+ <tt>http://qtsoftware.com:80/cgi-bin/test%20me.pl?cmd=Hello%20you</tt>
+
+ \table
+ \header \i Function \i Returns
+ \row \i \l protocol() \i "http"
+ \row \i \l host() \i "qtsoftware.com"
+ \row \i \l port() \i 80
+ \row \i \l path() \i "/cgi-bin/test&nbsp;me.pl"
+ \row \i \l fileName() \i "test&nbsp;me.pl"
+ \row \i \l query() \i "cmd=Hello%20you"
+ \endtable
+
+ Example:
+
+ <tt>http://doc.trolltech.com/qdockarea.html#lines</tt>
+
+ \table
+ \header \i Function \i Returns
+ \row \i \l protocol() \i "http"
+ \row \i \l host() \i "doc.trolltech.com"
+ \row \i \l fileName() \i "qdockarea.html"
+ \row \i \l ref() \i "lines"
+ \endtable
+
+ The individual parts of a URL can be set with setProtocol(),
+ setHost(), setPort(), setPath(), setFileName(), setRef() and
+ setQuery(). A URL could contain, for example, an ftp address which
+ requires a user name and password; these can be set with setUser()
+ and setPassword().
+
+ Because path is always encoded internally you must not use "%00"
+ in the path, although this is okay (but not recommended) for the
+ query.
+
+ Q3Url is normally used like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 0
+
+ You can then access and manipulate the various parts of the URL.
+
+ To make it easy to work with Q3Urls and QStrings, Q3Url implements
+ the necessary cast and assignment operators so you can do
+ following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 1
+
+ Use the static functions, encode() and decode() to encode or
+ decode a URL in a string. (They operate on the string in-place.)
+ The isRelativeUrl() static function returns true if the given
+ string is a relative URL.
+
+ If you want to use a URL to work on a hierarchical structure (e.g.
+ a local or remote filesystem), you might want to use the subclass
+ Q3UrlOperator.
+
+ \sa Q3UrlOperator
+*/
+
+
+/*!
+ Constructs an empty URL that is invalid.
+*/
+
+Q3Url::Q3Url()
+{
+ d = new Q3UrlPrivate;
+ d->isValid = false;
+ d->port = -1;
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Constructs a URL by parsing the string \a url.
+
+ If you pass a string like "/home/qt", the "file" protocol is
+ assumed.
+*/
+
+Q3Url::Q3Url( const QString& url )
+{
+ d = new Q3UrlPrivate;
+ d->protocol = QLatin1String("file");
+ d->port = -1;
+ parse( url );
+}
+
+/*!
+ Copy constructor. Copies the data of \a url.
+*/
+
+Q3Url::Q3Url( const Q3Url& url )
+{
+ d = new Q3UrlPrivate;
+ *d = *url.d;
+}
+
+/*!
+ Returns true if \a url is relative; otherwise returns false.
+*/
+
+bool Q3Url::isRelativeUrl( const QString &url )
+{
+ int colon = url.find( QLatin1String(":") );
+ int slash = url.find( QLatin1String("/") );
+
+ return ( slash != 0 && ( colon == -1 || ( slash != -1 && colon > slash ) ) );
+}
+
+/*!
+ Constructs an URL taking \a url as the base (context) and
+ \a relUrl as a relative URL to \a url. If \a relUrl is not relative,
+ \a relUrl is taken as the new URL.
+
+ For example, the path of
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 2
+ will be "/qt/srource/qt-2.1.0.tar.gz".
+
+ On the other hand,
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 3
+ will result in a new URL, "ftp://ftp.trolltech.com/usr/local",
+ because "/usr/local" isn't relative.
+
+ Similarly,
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 4
+ will result in a new URL, with "/usr/local" as the path
+ and "file" as the protocol.
+
+ Normally it is expected that the path of \a url points to a
+ directory, even if the path has no slash at the end. But if you
+ want the constructor to handle the last part of the path as a file
+ name if there is no slash at the end, and to let it be replaced by
+ the file name of \a relUrl (if it contains one), set \a checkSlash
+ to true.
+*/
+
+Q3Url::Q3Url( const Q3Url& url, const QString& relUrl, bool checkSlash )
+{
+ d = new Q3UrlPrivate;
+ QString rel = relUrl;
+ slashify( rel );
+
+ Q3Url urlTmp( url );
+ if ( !urlTmp.isValid() ) {
+ urlTmp.reset();
+ }
+ if ( isRelativeUrl( rel ) ) {
+ if ( rel[ 0 ] == QLatin1Char('#') ) {
+ *this = urlTmp;
+ rel.remove( (uint)0, 1 );
+ decode( rel );
+ setRef( rel );
+ } else if ( rel[ 0 ] == QLatin1Char('?') ) {
+ *this = urlTmp;
+ rel.remove( (uint)0, 1 );
+ setQuery( rel );
+ } else {
+ decode( rel );
+ *this = urlTmp;
+ setRef( QString() );
+ if ( checkSlash && d->cleanPath[(int)path().length()-1] != QLatin1Char('/') ) {
+ if ( isRelativeUrl( path() ) )
+ setEncodedPathAndQuery( rel );
+ else
+ setFileName( rel );
+ } else {
+ QString p = urlTmp.path();
+ if ( p.isEmpty() ) {
+ // allow URLs like "file:foo"
+ if ( !d->host.isEmpty() && !d->user.isEmpty() && !d->pass.isEmpty() )
+ p = QLatin1String("/");
+ }
+ if ( !p.isEmpty() && p.right(1)!=QLatin1String("/") )
+ p += QLatin1String("/");
+ p += rel;
+ d->path = p;
+ d->cleanPathDirty = true;
+ }
+ }
+ } else {
+ if ( rel[ 0 ] == QChar( QLatin1Char('/') ) ) {
+ *this = urlTmp;
+ setEncodedPathAndQuery( rel );
+ } else {
+ *this = rel;
+ }
+ }
+}
+
+/*!
+ Destructor.
+*/
+
+Q3Url::~Q3Url()
+{
+ delete d;
+ d = 0;
+}
+
+/*!
+ Returns the protocol of the URL. Typically, "file", "http", "ftp",
+ etc.
+
+ \sa setProtocol()
+*/
+
+QString Q3Url::protocol() const
+{
+ return d->protocol;
+}
+
+/*!
+ Sets the protocol of the URL to \a protocol. Typically, "file",
+ "http", "ftp", etc.
+
+ \sa protocol()
+*/
+
+void Q3Url::setProtocol( const QString& protocol )
+{
+ d->protocol = protocol;
+ if ( hasHost() )
+ d->isValid = true;
+}
+
+/*!
+ Returns the username of the URL.
+
+ \sa setUser() setPassword()
+*/
+
+QString Q3Url::user() const
+{
+ return d->user;
+}
+
+/*!
+ Sets the username of the URL to \a user.
+
+ \sa user() setPassword()
+*/
+
+void Q3Url::setUser( const QString& user )
+{
+ d->user = user;
+}
+
+/*!
+ Returns true if the URL contains a username; otherwise returns
+ false.
+
+ \sa setUser() setPassword()
+*/
+
+bool Q3Url::hasUser() const
+{
+ return !d->user.isEmpty();
+}
+
+/*!
+ Returns the password of the URL.
+
+ \warning Passwords passed in URLs are normally \e insecure; this
+ is due to the mechanism, not because of Qt.
+
+ \sa setPassword() setUser()
+*/
+
+QString Q3Url::password() const
+{
+ return d->pass;
+}
+
+/*!
+ Sets the password of the URL to \a pass.
+
+ \warning Passwords passed in URLs are normally \e insecure; this
+ is due to the mechanism, not because of Qt.
+
+ \sa password() setUser()
+*/
+
+void Q3Url::setPassword( const QString& pass )
+{
+ d->pass = pass;
+}
+
+/*!
+ Returns true if the URL contains a password; otherwise returns
+ false.
+
+ \warning Passwords passed in URLs are normally \e insecure; this
+ is due to the mechanism, not because of Qt.
+
+ \sa setPassword() setUser()
+*/
+
+bool Q3Url::hasPassword() const
+{
+ return !d->pass.isEmpty();
+}
+
+/*!
+ Returns the hostname of the URL.
+
+ \sa setHost() hasHost()
+*/
+
+QString Q3Url::host() const
+{
+ return d->host;
+}
+
+/*!
+ Sets the hostname of the URL to \a host.
+
+ \sa host() hasHost()
+*/
+
+void Q3Url::setHost( const QString& host )
+{
+ d->host = host;
+ if ( !d->protocol.isNull() && d->protocol != QLatin1String("file") )
+ d->isValid = true;
+}
+
+/*!
+ Returns true if the URL contains a hostname; otherwise returns
+ false.
+
+ \sa setHost()
+*/
+
+bool Q3Url::hasHost() const
+{
+ return !d->host.isEmpty();
+}
+
+/*!
+ Returns the port of the URL or -1 if no port has been set.
+
+ \sa setPort()
+*/
+
+int Q3Url::port() const
+{
+ return d->port;
+}
+
+/*!
+ Sets the port of the URL to \a port.
+
+ \sa port()
+*/
+
+void Q3Url::setPort( int port )
+{
+ d->port = port;
+}
+
+/*!
+ Returns true if the URL contains a port; otherwise returns false.
+
+ \sa setPort()
+*/
+
+bool Q3Url::hasPort() const
+{
+ return d->port >= 0;
+}
+
+/*!
+ Sets the path of the URL to \a path.
+
+ \sa path() hasPath()
+*/
+
+void Q3Url::setPath( const QString& path )
+{
+ d->path = path;
+ slashify( d->path );
+ d->cleanPathDirty = true;
+ d->isValid = true;
+}
+
+/*!
+ Returns true if the URL contains a path; otherwise returns false.
+
+ \sa path() setPath()
+*/
+
+bool Q3Url::hasPath() const
+{
+ return !d->path.isEmpty();
+}
+
+/*!
+ Sets the query of the URL to \a txt. \a txt must be encoded.
+
+ \sa query() encode()
+*/
+
+void Q3Url::setQuery( const QString& txt )
+{
+ d->queryEncoded = txt;
+}
+
+/*!
+ Returns the (encoded) query of the URL.
+
+ \sa setQuery() decode()
+*/
+
+QString Q3Url::query() const
+{
+ return d->queryEncoded;
+}
+
+/*!
+ Returns the (encoded) reference of the URL.
+
+ \sa setRef() hasRef() decode()
+*/
+
+QString Q3Url::ref() const
+{
+ return d->refEncoded;
+}
+
+/*!
+ Sets the reference of the URL to \a txt. \a txt must be encoded.
+
+ \sa ref() hasRef() encode()
+*/
+
+void Q3Url::setRef( const QString& txt )
+{
+ d->refEncoded = txt;
+}
+
+/*!
+ Returns true if the URL has a reference; otherwise returns false.
+
+ \sa setRef()
+*/
+
+bool Q3Url::hasRef() const
+{
+ return !d->refEncoded.isEmpty();
+}
+
+/*!
+ Returns true if the URL is valid; otherwise returns false. A URL
+ is invalid if it cannot be parsed, for example.
+*/
+
+bool Q3Url::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ Resets all parts of the URL to their default values and
+ invalidates it.
+*/
+
+void Q3Url::reset()
+{
+ d->protocol = QLatin1String("file");
+ d->user = QLatin1String("");
+ d->pass = QLatin1String("");
+ d->host = QLatin1String("");
+ d->path = QLatin1String("");
+ d->queryEncoded = QLatin1String("");
+ d->refEncoded = QLatin1String("");
+ d->isValid = true;
+ d->port = -1;
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Parses the \a url. Returns true on success; otherwise returns false.
+*/
+
+bool Q3Url::parse( const QString& url )
+{
+ QString url_( url );
+ slashify( url_ );
+
+ if ( url_.isEmpty() ) {
+ d->isValid = false;
+ return false;
+ }
+
+ d->cleanPathDirty = true;
+ d->isValid = true;
+ QString oldProtocol = d->protocol;
+ d->protocol.clear();
+
+ const int Init = 0;
+ const int Protocol = 1;
+ const int Separator1= 2; // :
+ const int Separator2= 3; // :/
+ const int Separator3= 4; // :// or more slashes
+ const int User = 5;
+ const int Pass = 6;
+ const int Host = 7;
+ const int Path = 8;
+ const int Ref = 9;
+ const int Query = 10;
+ const int Port = 11;
+ const int Done = 12;
+
+ const int InputAlpha= 1;
+ const int InputDigit= 2;
+ const int InputSlash= 3;
+ const int InputColon= 4;
+ const int InputAt = 5;
+ const int InputHash = 6;
+ const int InputQuery= 7;
+
+ static uchar table[ 12 ][ 8 ] = {
+ /* None InputAlpha InputDigit InputSlash InputColon InputAt InputHash InputQuery */
+ { 0, Protocol, 0, Path, 0, 0, 0, 0, }, // Init
+ { 0, Protocol, Protocol, 0, Separator1, 0, 0, 0, }, // Protocol
+ { 0, Path, Path, Separator2, 0, 0, 0, 0, }, // Separator1
+ { 0, Path, Path, Separator3, 0, 0, 0, 0, }, // Separator2
+ { 0, User, User, Separator3, Pass, Host, 0, 0, }, // Separator3
+ { 0, User, User, User, Pass, Host, User, User, }, // User
+ { 0, Pass, Pass, Pass, Pass, Host, Pass, Pass, }, // Pass
+ { 0, Host, Host, Path, Port, Host, Ref, Query, }, // Host
+ { 0, Path, Path, Path, Path, Path, Ref, Query, }, // Path
+ { 0, Ref, Ref, Ref, Ref, Ref, Ref, Query, }, // Ref
+ { 0, Query, Query, Query, Query, Query, Query, Query, }, // Query
+ { 0, 0, Port, Path, 0, 0, 0, 0, } // Port
+ };
+
+ bool relPath = false;
+
+ relPath = false;
+ bool forceRel = false;
+
+ // If ':' is at pos 1, we have only one letter
+ // before that separator => that's a drive letter!
+ if ( url_.length() >= 2 && url_[1] == QLatin1Char(':') )
+ relPath = forceRel = true;
+
+ int hasNoHost = -1;
+ int cs = url_.find( QLatin1String(":/") );
+ if ( cs != -1 ) // if a protocol is there, find out if there is a host or directly the path after it
+ hasNoHost = url_.find( QLatin1String("///"), cs );
+ table[ 4 ][ 1 ] = User;
+ table[ 4 ][ 2 ] = User;
+ if ( cs == -1 || forceRel ) { // we have a relative file
+ if ( url.find( QLatin1Char(':') ) == -1 || forceRel ) {
+ table[ 0 ][ 1 ] = Path;
+ // Filenames may also begin with a digit
+ table[ 0 ][ 2 ] = Path;
+ } else {
+ table[ 0 ][ 1 ] = Protocol;
+ }
+ relPath = true;
+ } else { // some checking
+ table[ 0 ][ 1 ] = Protocol;
+
+ // find the part between the protocol and the path as the meaning
+ // of that part is dependent on some chars
+ ++cs;
+ while ( url_[ cs ] == QLatin1Char('/') )
+ ++cs;
+ int slash = url_.find( QLatin1String("/"), cs );
+ if ( slash == -1 )
+ slash = url_.length() - 1;
+ QString tmp = url_.mid( cs, slash - cs + 1 );
+
+ if ( !tmp.isEmpty() ) { // if this part exists
+
+ // look for the @ in this part
+ int at = tmp.find( QLatin1String("@") );
+ if ( at != -1 )
+ at += cs;
+ // we have no @, which means host[:port], so directly
+ // after the protocol the host starts, or if the protocol
+ // is file or there were more than 2 slashes, it is the
+ // path
+ if ( at == -1 ) {
+ if ( url_.left( 4 ) == QLatin1String("file") || hasNoHost != -1 )
+ table[ 4 ][ 1 ] = Path;
+ else
+ table[ 4 ][ 1 ] = Host;
+ table[ 4 ][ 2 ] = table[ 4 ][ 1 ];
+ }
+ }
+ }
+
+ int state = Init; // parse state
+ int input; // input token
+
+ QChar c = url_[ 0 ];
+ int i = 0;
+ QString port;
+
+ for ( ;; ) {
+ switch ( c.latin1() ) {
+ case '?':
+ input = InputQuery;
+ break;
+ case '#':
+ input = InputHash;
+ break;
+ case '@':
+ input = InputAt;
+ break;
+ case ':':
+ input = InputColon;
+ break;
+ case '/':
+ input = InputSlash;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case '0':
+ input = InputDigit;
+ break;
+ default:
+ input = InputAlpha;
+ }
+
+ state = table[ state ][ input ];
+
+ switch ( state ) {
+ case Protocol:
+ d->protocol += c;
+ break;
+ case User:
+ d->user += c;
+ break;
+ case Pass:
+ d->pass += c;
+ break;
+ case Host:
+ d->host += c;
+ break;
+ case Path:
+ d->path += c;
+ break;
+ case Ref:
+ d->refEncoded += c;
+ break;
+ case Query:
+ d->queryEncoded += c;
+ break;
+ case Port:
+ port += c;
+ break;
+ default:
+ break;
+ }
+
+ ++i;
+ if ( i > (int)url_.length() - 1 || state == Done || state == 0 )
+ break;
+ c = url_[ i ];
+
+ }
+
+ if ( !port.isEmpty() ) {
+ port.remove( (uint)0, 1 );
+ d->port = port.toInt();
+ }
+
+ // error
+ if ( i < (int)url_.length() - 1 ) {
+ d->isValid = false;
+ return false;
+ }
+
+
+ if ( d->protocol.isEmpty() )
+ d->protocol = oldProtocol;
+
+ if ( d->path.isEmpty() )
+ d->path = QLatin1String("/");
+
+ // hack for windows
+ if ( d->path.length() == 2 && d->path[ 1 ] == QLatin1Char(':') )
+ d->path += QLatin1String("/");
+
+ // #### do some corrections, should be done nicer too
+ if ( !d->pass.isEmpty() ) {
+ if ( d->pass[ 0 ] == QLatin1Char(':') )
+ d->pass.remove( (uint)0, 1 );
+ decode( d->pass );
+ }
+ if ( !d->user.isEmpty() ) {
+ decode( d->user );
+ }
+ if ( !d->path.isEmpty() ) {
+ if ( d->path[ 0 ] == QLatin1Char('@') || d->path[ 0 ] == QLatin1Char(':') )
+ d->path.remove( (uint)0, 1 );
+ if ( d->path[ 0 ] != QLatin1Char('/') && !relPath && d->path[ 1 ] != QLatin1Char(':') )
+ d->path.prepend( QLatin1String("/") );
+ }
+ if ( !d->refEncoded.isEmpty() && d->refEncoded[ 0 ] == QLatin1Char('#') )
+ d->refEncoded.remove( (uint)0, 1 );
+ if ( !d->queryEncoded.isEmpty() && d->queryEncoded[ 0 ] == QLatin1Char('?') )
+ d->queryEncoded.remove( (uint)0, 1 );
+ if ( !d->host.isEmpty() && d->host[ 0 ] == QLatin1Char('@') )
+ d->host.remove( (uint)0, 1 );
+
+#if defined(Q_OS_WIN32)
+ // hack for windows file://machine/path syntax
+ if ( d->protocol == QLatin1String("file") ) {
+ if ( url.left( 7 ) == QLatin1String("file://") &&
+ d->path.length() > 1 && d->path[ 1 ] != QLatin1Char(':') )
+ d->path.prepend( QLatin1String("/") );
+ }
+#endif
+
+ decode( d->path );
+ d->cleanPathDirty = true;
+
+#if 0
+ qDebug( "URL: %s", url.latin1() );
+ qDebug( "protocol: %s", d->protocol.latin1() );
+ qDebug( "user: %s", d->user.latin1() );
+ qDebug( "pass: %s", d->pass.latin1() );
+ qDebug( "host: %s", d->host.latin1() );
+ qDebug( "path: %s", path().latin1() );
+ qDebug( "ref: %s", d->refEncoded.latin1() );
+ qDebug( "query: %s", d->queryEncoded.latin1() );
+ qDebug( "port: %d\n\n----------------------------\n\n", d->port );
+#endif
+
+ return true;
+}
+
+/*!
+ \overload
+
+ Parses \a url and assigns the resulting data to this class.
+
+ If you pass a string like "/home/qt" the "file" protocol will be
+ assumed.
+*/
+
+Q3Url& Q3Url::operator=( const QString& url )
+{
+ reset();
+ parse( url );
+
+ return *this;
+}
+
+/*!
+ Assigns the data of \a url to this class.
+*/
+
+Q3Url& Q3Url::operator=( const Q3Url& url )
+{
+ *d = *url.d;
+ return *this;
+}
+
+/*!
+ Compares this URL with \a url and returns true if they are equal;
+ otherwise returns false.
+*/
+
+bool Q3Url::operator==( const Q3Url& url ) const
+{
+ if ( !isValid() || !url.isValid() )
+ return false;
+
+ if ( d->protocol == url.d->protocol &&
+ d->user == url.d->user &&
+ d->pass == url.d->pass &&
+ d->host == url.d->host &&
+ d->path == url.d->path &&
+ d->queryEncoded == url.d->queryEncoded &&
+ d->refEncoded == url.d->refEncoded &&
+ d->isValid == url.d->isValid &&
+ d->port == url.d->port )
+ return true;
+
+ return false;
+}
+
+/*!
+ \overload
+
+ Compares this URL with \a url. \a url is parsed first. Returns
+ true if \a url is equal to this url; otherwise returns false.
+*/
+
+bool Q3Url::operator==( const QString& url ) const
+{
+ Q3Url u( url );
+ return ( *this == u );
+}
+
+/*!
+ Sets the file name of the URL to \a name. If this URL contains a
+ fileName(), the original file name is replaced by \a name.
+
+ See the documentation of fileName() for a more detailed discussion
+ of what is handled as file name and what is handled as a directory
+ path.
+
+ \sa fileName()
+*/
+
+void Q3Url::setFileName( const QString& name )
+{
+ QString fn( name );
+ slashify( fn );
+
+ while ( fn[ 0 ] == QLatin1Char( '/' ) )
+ fn.remove( (uint)0, 1 );
+
+ QString p;
+ if ( path().isEmpty() ) {
+ p = QLatin1String("/");
+ } else {
+ p = path();
+ int slash = p.findRev( QLatin1Char( '/' ) );
+ if ( slash == -1 ) {
+ p = QLatin1String("/");
+ } else if ( p[ (int)p.length() - 1 ] != QLatin1Char( '/' ) ) {
+ p.truncate( slash + 1 );
+ }
+ }
+
+ p += fn;
+ if ( !d->queryEncoded.isEmpty() )
+ p += QLatin1String("?") + d->queryEncoded;
+ setEncodedPathAndQuery( p );
+}
+
+/*!
+ Returns the encoded path and query.
+
+ \sa decode()
+*/
+
+QString Q3Url::encodedPathAndQuery()
+{
+ QString p = path();
+ if ( p.isEmpty() )
+ p = QLatin1String("/");
+
+ encode( p );
+
+ if ( !d->queryEncoded.isEmpty() ) {
+ p += QLatin1String("?");
+ p += d->queryEncoded;
+ }
+
+ return p;
+}
+
+/*!
+ Parses \a pathAndQuery for a path and query and sets those values.
+ The whole string must be encoded.
+
+ \sa encode()
+*/
+
+void Q3Url::setEncodedPathAndQuery( const QString& pathAndQuery )
+{
+ d->cleanPathDirty = true;
+ int pos = pathAndQuery.find( QLatin1Char('?') );
+ if ( pos == -1 ) {
+ d->path = pathAndQuery;
+ d->queryEncoded = QLatin1String("");
+ } else {
+ d->path = pathAndQuery.left( pos );
+ d->queryEncoded = pathAndQuery.mid( pos + 1 );
+ }
+
+ decode( d->path );
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Returns the path of the URL. If \a correct is true, the path is
+ cleaned (deals with too many or too few slashes, cleans things
+ like "/../..", etc). Otherwise path() returns exactly the path
+ that was parsed or set.
+
+ \sa setPath() hasPath()
+*/
+QString Q3Url::path( bool correct ) const
+{
+ if ( !correct )
+ return d->path;
+
+ if ( d->cleanPathDirty ) {
+ bool check = true;
+ if ( QDir::isRelativePath( d->path ) ) {
+ d->cleanPath = d->path;
+ } else if ( isLocalFile() ) {
+#if defined(Q_OS_WIN32)
+ // hack for stuff like \\machine\path and //machine/path on windows
+ if ( ( d->path.left( 1 ) == QLatin1String("/") || d->path.left( 1 ) == QLatin1String("\\") ) &&
+ d->path.length() > 1 ) {
+ d->cleanPath = d->path;
+ bool share = (d->cleanPath[0] == QLatin1Char('\\') && d->cleanPath[1] == QLatin1Char('\\')) ||
+ (d->cleanPath[0] == QLatin1Char('/') && d->cleanPath[1] == QLatin1Char('/'));
+ slashify( d->cleanPath, false );
+ d->cleanPath = QDir::cleanDirPath( d->cleanPath );
+ if ( share ) {
+ check = false;
+ while (d->cleanPath.at(0) != QLatin1Char('/') || d->cleanPath.at(1) != QLatin1Char('/'))
+ d->cleanPath.prepend(QLatin1String("/"));
+ }
+ }
+#endif
+ if ( check ) {
+ QFileInfo fi( d->path );
+ if ( !fi.exists() )
+ d->cleanPath = d->path;
+ else if ( fi.isDir() ) {
+ QString canPath = QDir( d->path ).canonicalPath();
+ QString dir;
+ if ( qt_resolve_symlinks && !canPath.isNull() )
+ dir = QDir::cleanDirPath( canPath );
+ else
+ dir = QDir::cleanDirPath( QDir( d->path ).absPath() );
+ dir += QLatin1String("/");
+ if ( dir == QLatin1String("//") )
+ d->cleanPath = QLatin1String("/");
+ else
+ d->cleanPath = dir;
+ } else {
+ QString p =
+ QDir::cleanDirPath( (qt_resolve_symlinks ?
+ fi.dir().canonicalPath() :
+ fi.dir().absPath()) );
+ d->cleanPath = p + QLatin1String("/") + fi.fileName();
+ }
+ }
+ } else {
+ if ( d->path != QLatin1String("/") && d->path[ (int)d->path.length() - 1 ] == QLatin1Char('/') )
+ d->cleanPath = QDir::cleanDirPath( d->path ) + QLatin1String("/");
+ else
+ d->cleanPath = QDir::cleanDirPath( d->path );
+ }
+
+ if ( check )
+ slashify( d->cleanPath, false );
+ d->cleanPathDirty = false;
+ }
+
+ return d->cleanPath;
+}
+
+/*!
+ Returns true if the URL is a local file; otherwise returns false.
+*/
+
+bool Q3Url::isLocalFile() const
+{
+ return d->protocol == QLatin1String("file");
+}
+
+/*!
+ Returns the file name of the URL. If the path of the URL doesn't
+ have a slash at the end, the part between the last slash and the
+ end of the path string is considered to be the file name. If the
+ path has a slash at the end, an empty string is returned here.
+
+ \sa setFileName()
+*/
+
+QString Q3Url::fileName() const
+{
+ if ( d->path.isEmpty() || d->path.endsWith( QLatin1String("/") )
+#ifdef Q_WS_WIN
+ || d->path.endsWith( QLatin1String("\\") )
+#endif
+ )
+ return QString();
+
+ return QFileInfo( d->path ).fileName();
+}
+
+/*!
+ Adds the path \a pa to the path of the URL.
+
+ \sa setPath() hasPath()
+*/
+
+void Q3Url::addPath( const QString& pa )
+{
+ if ( pa.isEmpty() )
+ return;
+
+ QString p( pa );
+ slashify( p );
+
+ if ( path().isEmpty() ) {
+ if ( p[ 0 ] != QLatin1Char( '/' ) )
+ d->path = QLatin1String("/") + p;
+ else
+ d->path = p;
+ } else {
+ if ( p[ 0 ] != QLatin1Char( '/' ) && d->path[ (int)d->path.length() - 1 ] != QLatin1Char('/') )
+ d->path += QLatin1String("/") + p;
+ else
+ d->path += p;
+ }
+ d->cleanPathDirty = true;
+}
+
+/*!
+ Returns the directory path of the URL. This is the part of the
+ path of the URL without the fileName(). See the documentation of
+ fileName() for a discussion of what is handled as file name and
+ what is handled as directory path.
+
+ \sa setPath() hasPath()
+*/
+
+QString Q3Url::dirPath() const
+{
+ if ( path().isEmpty() )
+ return QString();
+
+ QString s = path();
+ int pos = s.findRev( QLatin1Char('/') );
+ if ( pos == -1 ) {
+ return QString::fromLatin1( "." );
+ } else {
+ if ( pos == 0 )
+ return QString::fromLatin1( "/" );
+ return s.left( pos );
+ }
+}
+
+/*!
+ Encodes the \a url in-place into UTF-8. For example
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 5
+
+ \sa decode()
+*/
+
+void Q3Url::encode( QString& url )
+{
+ if ( url.isEmpty() )
+ return;
+
+ Q3CString curl = url.utf8();
+ int oldlen = curl.length();
+
+ const Q3CString special( "+<>#@\"&%$:,;?={}|^~[]\'`\\ \n\t\r" );
+ QString newUrl;
+ int newlen = 0;
+
+ for ( int i = 0; i < oldlen ;++i ) {
+ uchar inCh = (uchar)curl[ i ];
+
+ if ( inCh >= 128 || special.contains(inCh) ) {
+ newUrl[ newlen++ ] = QLatin1Char( '%' );
+
+ ushort c = inCh / 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newUrl[ newlen++ ] = c;
+
+ c = inCh % 16;
+ c += c > 9 ? 'A' - 10 : '0';
+ newUrl[ newlen++ ] = c;
+ } else {
+ newUrl[ newlen++ ] = inCh;
+ }
+ }
+
+ url = newUrl;
+}
+
+static uchar hex_to_int( uchar c )
+{
+ if ( c >= 'A' && c <= 'F' )
+ return c - 'A' + 10;
+ if ( c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if ( c >= '0' && c <= '9')
+ return c - '0';
+ return 0;
+}
+
+/*!
+ Decodes the \a url in-place into UTF-8. For example
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3url.cpp 6
+
+ \sa encode()
+*/
+
+void Q3Url::decode( QString& url )
+{
+ if ( url.isEmpty() )
+ return;
+
+ int newlen = 0;
+ Q3CString curl = url.utf8();
+ int oldlen = curl.length();
+
+ Q3CString newUrl(oldlen);
+
+ int i = 0;
+ while ( i < oldlen ) {
+ uchar c = (uchar)curl[ i++ ];
+ if ( c == '%' && i <= oldlen - 2 ) {
+ c = hex_to_int( (uchar)curl[ i ] ) * 16 + hex_to_int( (uchar)curl[ i + 1 ] );
+ i += 2;
+ }
+ newUrl [ newlen++ ] = c;
+ }
+ newUrl.truncate( newlen );
+
+ url = QString::fromUtf8(newUrl.data());
+}
+
+
+/*!
+ Composes a string version of the URL and returns it. If \a
+ encodedPath is true the path in the returned string is encoded. If
+ \a forcePrependProtocol is true and \a encodedPath looks like a
+ local filename, the "file:/" protocol is also prepended.
+
+ \sa encode() decode()
+*/
+
+QString Q3Url::toString( bool encodedPath, bool forcePrependProtocol ) const
+{
+ QString res, p = path();
+ if ( encodedPath )
+ encode( p );
+
+ if ( isLocalFile() ) {
+ if ( forcePrependProtocol )
+ res = d->protocol + QLatin1String(":") + p;
+ else
+ res = p;
+ } else if ( d->protocol == QLatin1String("mailto") ) {
+ res = d->protocol + QLatin1String(":") + p;
+ } else {
+ res = d->protocol + QLatin1String("://");
+ if ( !d->user.isEmpty() || !d->pass.isEmpty() ) {
+ QString tmp;
+ if ( !d->user.isEmpty() ) {
+ tmp = d->user;
+ encode( tmp );
+ res += tmp;
+ }
+ if ( !d->pass.isEmpty() ) {
+ tmp = d->pass;
+ encode( tmp );
+ res += QLatin1String(":") + tmp;
+ }
+ res += QLatin1String("@");
+ }
+ res += d->host;
+ if ( d->port != -1 )
+ res += QLatin1String(":") + QString( QLatin1String("%1") ).arg( d->port );
+ if ( !p.isEmpty() ) {
+ if ( !d->host.isEmpty() && p[0]!= QLatin1Char( '/' ) )
+ res += QLatin1String("/");
+ res += p;
+ }
+ }
+
+ if ( !d->refEncoded.isEmpty() )
+ res += QLatin1String("#") + d->refEncoded;
+ if ( !d->queryEncoded.isEmpty() )
+ res += QLatin1String("?") + d->queryEncoded;
+
+ return res;
+}
+
+/*!
+ Composes a string version of the URL and returns it.
+
+ \sa Q3Url::toString()
+*/
+
+Q3Url::operator QString() const
+{
+ return toString();
+}
+
+/*!
+ Changes the directory to one directory up. This function always returns
+ true.
+
+ \sa setPath()
+*/
+
+bool Q3Url::cdUp()
+{
+ d->path += QLatin1String("/..");
+ d->cleanPathDirty = true;
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_URL
diff --git a/src/qt3support/network/q3url.h b/src/qt3support/network/q3url.h
new file mode 100644
index 0000000000..61c25b14b2
--- /dev/null
+++ b/src/qt3support/network/q3url.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3URL_H
+#define Q3URL_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_URL
+
+class Q3UrlPrivate;
+
+class Q_COMPAT_EXPORT Q3Url
+{
+public:
+ Q3Url();
+ Q3Url( const QString& url );
+ Q3Url( const Q3Url& url );
+ Q3Url( const Q3Url& url, const QString& relUrl, bool checkSlash = false );
+ virtual ~Q3Url();
+
+ QString protocol() const;
+ virtual void setProtocol( const QString& protocol );
+
+ QString user() const;
+ virtual void setUser( const QString& user );
+ bool hasUser() const;
+
+ QString password() const;
+ virtual void setPassword( const QString& pass );
+ bool hasPassword() const;
+
+ QString host() const;
+ virtual void setHost( const QString& user );
+ bool hasHost() const;
+
+ int port() const;
+ virtual void setPort( int port );
+ bool hasPort() const;
+
+ QString path( bool correct = true ) const;
+ virtual void setPath( const QString& path );
+ bool hasPath() const;
+
+ virtual void setEncodedPathAndQuery( const QString& enc );
+ QString encodedPathAndQuery();
+
+ virtual void setQuery( const QString& txt );
+ QString query() const;
+
+ QString ref() const;
+ virtual void setRef( const QString& txt );
+ bool hasRef() const;
+
+ bool isValid() const;
+ bool isLocalFile() const;
+
+ virtual void addPath( const QString& path );
+ virtual void setFileName( const QString& txt );
+
+ QString fileName() const;
+ QString dirPath() const;
+
+ Q3Url& operator=( const Q3Url& url );
+ Q3Url& operator=( const QString& url );
+
+ bool operator==( const Q3Url& url ) const;
+ bool operator==( const QString& url ) const;
+
+ static void decode( QString& url );
+ static void encode( QString& url );
+
+ operator QString() const;
+ virtual QString toString( bool encodedPath = false, bool forcePrependProtocol = true ) const;
+
+ virtual bool cdUp();
+
+ static bool isRelativeUrl( const QString &url );
+
+protected:
+ virtual void reset();
+ virtual bool parse( const QString& url );
+
+private:
+ Q3UrlPrivate *d;
+
+};
+
+#endif // QT_NO_URL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3URL_H
diff --git a/src/qt3support/network/q3urloperator.cpp b/src/qt3support/network/q3urloperator.cpp
new file mode 100644
index 0000000000..3f334a865b
--- /dev/null
+++ b/src/qt3support/network/q3urloperator.cpp
@@ -0,0 +1,1212 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3urloperator.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+#include "qurlinfo.h"
+#include "q3networkprotocol.h"
+#include "qmap.h"
+#include "qdir.h"
+#include "q3ptrdict.h"
+#include "qpointer.h"
+#include "q3valuelist.h"
+
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define Q3URLOPERATOR_DEBUG
+
+class Q3UrlOperatorPrivate
+{
+public:
+ Q3UrlOperatorPrivate()
+ {
+ oldOps.setAutoDelete( false );
+ networkProtocol = 0;
+ nameFilter = QLatin1String("*");
+ currPut = 0;
+ }
+
+ ~Q3UrlOperatorPrivate()
+ {
+ delete networkProtocol;
+ while ( oldOps.first() ) {
+ oldOps.first()->free();
+ oldOps.removeFirst();
+ }
+ }
+
+ QMap<QString, QUrlInfo> entryMap;
+ Q3NetworkProtocol *networkProtocol;
+ QString nameFilter;
+ QDir dir;
+
+ // maps needed for copy/move operations
+ Q3PtrDict<Q3NetworkOperation> getOpPutOpMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpPutProtMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpGetProtMap;
+ Q3PtrDict<Q3NetworkOperation> getOpRemoveOpMap;
+ QPointer<Q3NetworkProtocol> currPut;
+ QStringList waitingCopies;
+ QString waitingCopiesDest;
+ bool waitingCopiesMove;
+ Q3PtrList< Q3NetworkOperation > oldOps;
+};
+
+/*!
+ \class Q3UrlOperator
+
+ \brief The Q3UrlOperator class provides common operations on URLs.
+
+ \compat
+
+ \module network
+
+ This class operates on hierarchical structures (such as
+ filesystems) using URLs. Its API facilitates all the common
+ operations:
+ \table
+ \header \i Operation \i Function
+ \row \i List files \i \l listChildren()
+ \row \i Make a directory \i \l mkdir()
+ \row \i Remove a file \i \l remove()
+ \row \i Rename a file \i \l rename()
+ \row \i Get a file \i \l get()
+ \row \i Put a file \i \l put()
+ \row \i Copy a file \i \l copy()
+ \endtable
+
+ You can obtain additional information about the URL with isDir()
+ and info(). If a directory is to be traversed using
+ listChildren(), a name filter can be set with setNameFilter().
+
+ A Q3UrlOperator can be used like this, for example to download a
+ file (and assuming that the FTP protocol is registered):
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 0
+
+ If you want to be notified about success/failure, progress, etc.,
+ you can connect to Q3UrlOperator's signals, e.g. to start(),
+ newChildren(), createdDirectory(), removed(), data(),
+ dataTransferProgress(), startedNextCopy(),
+ connectionStateChanged(), finished(), etc. A network operation can
+ be stopped with stop().
+
+ The class uses the functionality of registered network protocols
+ to perform these operations. Depending of the protocol of the URL,
+ it uses an appropriate network protocol class for the operations.
+ Each of the operation functions of Q3UrlOperator creates a
+ Q3NetworkOperation object that describes the operation and puts it
+ into the operation queue for the network protocol used. If no
+ suitable protocol could be found (because no implementation of the
+ necessary network protocol is registered), the URL operator emits
+ errors. Not every protocol supports every operation, but error
+ handling deals with this problem.
+
+ To register the available network protocols, use the
+ qInitNetworkProtocols() function. The protocols currently
+ supported are:
+ \list
+ \i \link Q3Ftp FTP\endlink,
+ \i \link Q3Http HTTP\endlink,
+ \i \link Q3LocalFs local file system\endlink.
+ \endlist
+
+ \sa Q3NetworkProtocol, Q3NetworkOperation
+*/
+
+/*!
+ \fn void Q3UrlOperator::newChildren( const Q3ValueList<QUrlInfo> &i, Q3NetworkOperation *op )
+
+ This signal is emitted after listChildren() was called and new
+ children (i.e. files) have been read from a list of files. \a i
+ holds the information about the new files. \a op is a pointer
+ to the operation object which contains all the information about
+ the operation, including the state.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+
+/*!
+ \fn void Q3UrlOperator::finished( Q3NetworkOperation *op )
+
+ This signal is emitted when an operation of some sort finishes,
+ whether with success or failure. \a op is a pointer to the
+ operation object, which contains all the information, including
+ the state, of the operation which has been finished. Check the
+ state and error code of the operation object to see whether or not
+ the operation was successful.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::start( Q3NetworkOperation *op )
+
+ Some operations (such as listChildren()) emit this signal when
+ they start processing the operation. \a op is a pointer to the
+ operation object which contains all the information about the
+ operation, including the state.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::createdDirectory( const QUrlInfo &i, Q3NetworkOperation *op )
+
+ This signal is emitted when mkdir() succeeds and the directory has
+ been created. \a i holds the information about the new directory.
+
+ \a op is a pointer to the operation object, which contains all the
+ information about the operation, including the state.
+ \c op->arg(0) holds the new directory's name.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::removed( Q3NetworkOperation *op )
+
+ This signal is emitted when remove() has been successful and the
+ file has been removed.
+
+ \a op is a pointer to the operation object which contains all the
+ information about the operation, including the state.
+ \c op->arg(0) holds the name of the file that was removed.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::itemChanged( Q3NetworkOperation *op )
+
+ This signal is emitted whenever a file which is a child of the URL
+ has been changed, for example by successfully calling rename().
+ \a op is a pointer to the operation object which contains all the
+ information about the operation, including the state.
+ \c op->arg(0) holds the original file name and \c op->arg(1) holds
+ the new file name (if it was changed).
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::data( const QByteArray &data, Q3NetworkOperation *op )
+
+ This signal is emitted when new \a data has been received after calling
+ get() or put().
+ \a op is a pointer to the operation object which contains all
+ the information about the operation, including the state.
+ \c op->arg(0) holds the name of the file whose data is retrieved
+ and op->rawArg(1) holds the (raw) data.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *op )
+
+ This signal is emitted during data transfer (using put() or
+ get()). \a bytesDone specifies how many bytes of \a bytesTotal have
+ been transferred. More information about the operation is stored in
+ \a op, a pointer to the network operation that is processed.
+ \a bytesTotal may be -1, which means that the total number of bytes
+ is not known.
+
+ \sa Q3NetworkOperation, Q3NetworkProtocol
+*/
+
+/*!
+ \fn void Q3UrlOperator::startedNextCopy( const Q3PtrList<Q3NetworkOperation> &lst )
+
+ This signal is emitted if copy() starts a new copy operation. \a
+ lst contains all Q3NetworkOperations related to this copy
+ operation.
+
+ \sa copy()
+*/
+
+/*!
+ \fn void Q3UrlOperator::connectionStateChanged( int state, const QString &data )
+
+ This signal is emitted whenever the URL operator's connection
+ state changes. \a state describes the new state, which is a
+ \l{Q3NetworkProtocol::ConnectionState} value.
+
+ \a data is a string that describes the change of the connection.
+ This can be used to display a message to the user.
+*/
+
+/*!
+ Constructs a Q3UrlOperator with an empty (i.e. invalid) URL.
+*/
+
+Q3UrlOperator::Q3UrlOperator()
+ : Q3Url()
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 1" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+}
+
+/*!
+ Constructs a Q3UrlOperator using \a url and parses this string.
+
+ If you pass strings like "/home/qt" the "file" protocol is
+ assumed.
+*/
+
+Q3UrlOperator::Q3UrlOperator( const QString &url )
+ : Q3Url( url )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 2" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+ getNetworkProtocol();
+}
+
+/*!
+ Constructs a copy of \a url.
+*/
+
+Q3UrlOperator::Q3UrlOperator( const Q3UrlOperator& url )
+ : QObject(), Q3Url( url )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 3" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+ *d = *url.d;
+
+ d->networkProtocol = 0;
+ getNetworkProtocol();
+ d->nameFilter = QLatin1String("*");
+ d->currPut = 0;
+}
+
+/*!
+ Constructs a Q3UrlOperator. The URL on which this Q3UrlOperator
+ operates is constructed out of the arguments \a url, \a relUrl and
+ \a checkSlash: see the corresponding Q3Url constructor for an
+ explanation of these arguments.
+*/
+
+Q3UrlOperator::Q3UrlOperator( const Q3UrlOperator& url, const QString& relUrl, bool checkSlash )
+ : Q3Url( url, relUrl, checkSlash )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: cstr 4" );
+#endif
+ d = new Q3UrlOperatorPrivate;
+ if ( relUrl == QLatin1String(".") )
+ *d = *url.d;
+
+ d->networkProtocol = 0;
+ getNetworkProtocol();
+ d->currPut = 0;
+}
+
+/*!
+ Destructor.
+*/
+
+Q3UrlOperator::~Q3UrlOperator()
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: dstr" );
+#endif
+ delete d;
+}
+
+/*!
+ This private function is used by the simple operation functions,
+ i.e. listChildren(), mkdir(), remove(), rename(), get() and put(),
+ to really start the operation. \a op is a pointer to the network
+ operation that should be started. Returns \a op on success;
+ otherwise returns 0.
+*/
+const Q3NetworkOperation *Q3UrlOperator::startOperation( Q3NetworkOperation *op )
+{
+ if ( !d->networkProtocol )
+ getNetworkProtocol();
+
+ if ( d->networkProtocol && (d->networkProtocol->supportedOperations()&op->operation()) ) {
+ d->networkProtocol->addOperation( op );
+ if ( op->operation() == Q3NetworkProtocol::OpListChildren )
+ clearEntries();
+ return op;
+ }
+
+ // error
+ QString msg;
+ if ( !d->networkProtocol ) {
+ msg = tr( "The protocol `%1' is not supported" ).arg( protocol() );
+ } else {
+ switch ( op->operation() ) {
+ case Q3NetworkProtocol::OpListChildren:
+ msg = tr( "The protocol `%1' does not support listing directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpMkDir:
+ msg = tr( "The protocol `%1' does not support creating new directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpRemove:
+ msg = tr( "The protocol `%1' does not support removing files or directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpRename:
+ msg = tr( "The protocol `%1' does not support renaming files or directories" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpGet:
+ msg = tr( "The protocol `%1' does not support getting files" ).arg( protocol() );
+ break;
+ case Q3NetworkProtocol::OpPut:
+ msg = tr( "The protocol `%1' does not support putting files" ).arg( protocol() );
+ break;
+ default:
+ // this should never happen
+ break;
+ }
+ }
+ op->setState( Q3NetworkProtocol::StFailed );
+ op->setProtocolDetail( msg );
+ op->setErrorCode( (int)Q3NetworkProtocol::ErrUnsupported );
+ emit finished( op );
+ deleteOperation( op );
+ return 0;
+}
+
+/*!
+ Starts listing the children of this URL (e.g. the files in the
+ directory). The start() signal is emitted before the first entry
+ is listed and finished() is emitted after the last one. The
+ newChildren() signal is emitted for each list of new entries. If
+ an error occurs, the signal finished() is emitted, so be sure to
+ check the state of the network operation pointer.
+
+ Because the operation may not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ The path of this Q3UrlOperator must to point to a directory
+ (because the children of this directory will be listed), not to a
+ file.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::listChildren()
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpListChildren, QString(), QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ Tries to create a directory (child) with the name \a dirname. If
+ it is successful, a newChildren() signal with the new child is
+ emitted, and the createdDirectory() signal with the information
+ about the new child is also emitted. The finished() signal (with
+ success or failure) is emitted after the operation has been
+ processed, so check the state of the network operation object to
+ see whether or not the operation was successful.
+
+ Because the operation will not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ The path of this Q3UrlOperator must to point to a directory (not a
+ file) because the new directory will be created in this path.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::mkdir( const QString &dirname )
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpMkDir, dirname, QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ Tries to remove the file (child) \a filename. If it succeeds the
+ removed() signal is emitted. finished() (with success or failure)
+ is also emitted after the operation has been processed, so check
+ the state of the network operation object to see whether or not
+ the operation was successful.
+
+ Because the operation will not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ The path of this Q3UrlOperator must point to a directory; because
+ if \a filename is relative, it will try to remove it in this
+ directory.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::remove( const QString &filename )
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpRemove, filename, QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ Tries to rename the file (child) called \a oldname to \a newname.
+ If it succeeds, the itemChanged() signal is emitted. finished()
+ (with success or failure) is also emitted after the operation has
+ been processed, so check the state of the network operation object
+ to see whether or not the operation was successful.
+
+ Because the operation may not be executed immediately, a pointer
+ to the Q3NetworkOperation object created by this function is
+ returned. This object contains all the data about the operation
+ and is used to refer to this operation later (e.g. in the signals
+ that are emitted by the Q3UrlOperator). The return value can also
+ be 0 if the operation object couldn't be created.
+
+ This path of this Q3UrlOperator must to point to a directory
+ because \a oldname and \a newname are handled relative to this
+ directory.
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::rename( const QString &oldname, const QString &newname )
+{
+ if ( !checkValid() )
+ return 0;
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpRename, oldname, newname, QString() );
+ return startOperation( res );
+}
+
+/*!
+ Copies the file \a from to \a to. If \a move is true, the file is
+ moved (copied and removed). \a from must point to a file and \a to
+ must point to a directory (into which \a from is copied) unless \a
+ toPath is set to false. If \a toPath is set to false then the \a
+ to variable is assumed to be the absolute file path (destination
+ file path + file name). The copying is done using the get() and
+ put() operations. If you want to be notified about the progress of
+ the operation, connect to the dataTransferProgress() signal. Bear
+ in mind that the get() and put() operations emit this signal
+ through the Q3UrlOperator. The number of transferred bytes and the
+ total bytes that you receive as arguments in this signal do not
+ relate to the the whole copy operation; they relate first to the
+ get() and then to the put() operation. Always check what type of
+ operation the signal comes from; this is given in the signal's
+ last argument.
+
+ At the end, finished() (with success or failure) is emitted, so
+ check the state of the network operation object to see whether or
+ not the operation was successful.
+
+ Because a move or copy operation consists of multiple operations
+ (get(), put() and maybe remove()), this function doesn't return a
+ single Q3NetworkOperation, but rather a list of them. They are in
+ the order: get(), put() and (if applicable) remove().
+
+ \sa get(), put()
+*/
+
+Q3PtrList<Q3NetworkOperation> Q3UrlOperator::copy( const QString &from, const QString &to, bool move, bool toPath )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: copy %s %s %d", from.latin1(), to.latin1(), move );
+#endif
+
+ Q3PtrList<Q3NetworkOperation> ops;
+ ops.setAutoDelete( false );
+
+ Q3UrlOperator *uFrom = new Q3UrlOperator( *this, from );
+ Q3UrlOperator *uTo = new Q3UrlOperator( to );
+
+ // prepare some string for later usage
+ QString frm = *uFrom;
+ QString file = uFrom->fileName();
+
+ if (frm == to + file)
+ return ops;
+
+ file.prepend( QLatin1String("/") );
+
+ // uFrom and uTo are deleted when the Q3NetworkProtocol deletes itself via
+ // autodelete
+ uFrom->getNetworkProtocol();
+ uTo->getNetworkProtocol();
+ Q3NetworkProtocol *gProt = uFrom->d->networkProtocol;
+ Q3NetworkProtocol *pProt = uTo->d->networkProtocol;
+
+ uFrom->setPath( uFrom->dirPath() );
+
+ if ( gProt && (gProt->supportedOperations()&Q3NetworkProtocol::OpGet) &&
+ pProt && (pProt->supportedOperations()&Q3NetworkProtocol::OpPut) ) {
+
+ connect( gProt, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ this, SLOT(copyGotData(QByteArray,Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(continueCopy(Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( gProt, SIGNAL(connectionStateChanged(int,QString)),
+ this, SIGNAL(connectionStateChanged(int,QString)) );
+
+ connect( pProt, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)),
+ this, SIGNAL(dataTransferProgress(int,int,Q3NetworkOperation*)) );
+ connect( pProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SIGNAL(finished(Q3NetworkOperation*)) );
+ connect( pProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(finishedCopy()) );
+
+ Q3NetworkOperation *opGet = new Q3NetworkOperation( Q3NetworkProtocol::OpGet, frm, QString(), QString() );
+ ops.append( opGet );
+ gProt->addOperation( opGet );
+
+
+ QString toFile = to + file;
+ if (!toPath)
+ toFile = to;
+
+ Q3NetworkOperation *opPut = new Q3NetworkOperation( Q3NetworkProtocol::OpPut, toFile, QString(), QString() );
+ ops.append( opPut );
+
+ d->getOpPutProtMap.insert( (void*)opGet, pProt );
+ d->getOpGetProtMap.insert( (void*)opGet, gProt );
+ d->getOpPutOpMap.insert( (void*)opGet, opPut );
+
+ if ( move && (gProt->supportedOperations()&Q3NetworkProtocol::OpRemove) ) {
+ gProt->setAutoDelete( false );
+
+ Q3NetworkOperation *opRm = new Q3NetworkOperation( Q3NetworkProtocol::OpRemove, frm, QString(), QString() );
+ ops.append( opRm );
+ d->getOpRemoveOpMap.insert( (void*)opGet, opRm );
+ } else {
+ gProt->setAutoDelete( true );
+ }
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: copy operation should start now..." );
+#endif
+ return ops;
+ } else {
+ QString msg;
+ if ( !gProt ) {
+ msg = tr( "The protocol `%1' is not supported" ).arg( uFrom->protocol() );
+ } else if ( gProt->supportedOperations() & Q3NetworkProtocol::OpGet ) {
+ msg = tr( "The protocol `%1' does not support copying or moving files or directories" ).arg( uFrom->protocol() );
+ } else if ( !pProt ) {
+ msg = tr( "The protocol `%1' is not supported" ).arg( uTo->protocol() );
+ } else {
+ msg = tr( "The protocol `%1' does not support copying or moving files or directories" ).arg( uTo->protocol() );
+ }
+ delete uFrom;
+ delete uTo;
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpGet, frm, to, QString() );
+ res->setState( Q3NetworkProtocol::StFailed );
+ res->setProtocolDetail( msg );
+ res->setErrorCode( (int)Q3NetworkProtocol::ErrUnsupported );
+ emit finished( res );
+ deleteOperation( res );
+ }
+
+ return ops;
+}
+
+/*!
+ \overload
+
+ Copies the \a files to the directory \a dest. If \a move is true
+ the files are moved, not copied. \a dest must point to a
+ directory.
+
+ This function calls copy() for each entry in \a files in turn. You
+ don't get a result from this function; each time a new copy
+ begins, startedNextCopy() is emitted, with a list of
+ Q3NetworkOperations that describe the new copy operation.
+*/
+
+void Q3UrlOperator::copy( const QStringList &files, const QString &dest,
+ bool move )
+{
+ d->waitingCopies = files;
+ d->waitingCopiesDest = dest;
+ d->waitingCopiesMove = move;
+
+ finishedCopy();
+}
+
+/*!
+ Returns true if the URL is a directory; otherwise returns false.
+ This may not always work correctly, if the protocol of the URL is
+ something other than file (local filesystem). If you pass a bool
+ pointer as the \a ok argument, *\a ok is set to true if the result
+ of this function is known to be correct, and to false otherwise.
+*/
+
+bool Q3UrlOperator::isDir( bool *ok )
+{
+ if ( ok )
+ *ok = true;
+ if ( isLocalFile() ) {
+ if ( QFileInfo( path() ).isDir() )
+ return true;
+ else
+ return false;
+ }
+
+ if ( d->entryMap.contains( QLatin1String(".") ) ) {
+ return d->entryMap[ QLatin1String(".") ].isDir();
+ }
+ // #### can assume that we are a directory?
+ if ( ok )
+ *ok = false;
+ return true;
+}
+
+/*!
+ Tells the network protocol to get data from \a location or, if
+ it is empty, to get data from the location to which this
+ URL points (see Q3Url::fileName() and Q3Url::encodedPathAndQuery()).
+ What happens then depends on the network protocol. The data()
+ signal is emitted when data comes in. Because it's unlikely that
+ all data will come in at once, it is common for multiple data()
+ signals to be emitted. The dataTransferProgress() signal is
+ emitted while processing the operation. At the end, finished()
+ (with success or failure) is emitted, so check the state of the
+ network operation object to see whether or not the operation was
+ successful.
+
+ If \a location is empty, the path of this Q3UrlOperator
+ should point to a file when you use this operation. If \a location
+ is not empty, it can be a relative URL (a child of the path to
+ which the Q3UrlOperator points) or an absolute URL.
+
+ For example, to get a web page you might do something like this:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 1
+
+ For most other operations, the path of the Q3UrlOperator must point
+ to a directory. If you want to download a file you could do the
+ following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 2
+
+ This will get the data of ftp://ftp.whatever.org/pub/a_file.txt.
+
+ \e Never do anything like this:
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 3
+
+ If \a location is not empty and relative it must not contain any
+ queries or references, just the name of a child. So if you need to
+ specify a query or reference, do it as shown in the first example
+ or specify the full URL (such as
+ http://www.whatever.org/cgi-bin/search.pl?cmd=Hello) as \a location.
+
+ \sa copy()
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::get( const QString &location )
+{
+ Q3Url u( *this );
+ if ( !location.isEmpty() )
+ u = Q3Url( *this, location );
+
+ if ( !u.isValid() )
+ return 0;
+
+ if ( !d->networkProtocol ) {
+ setProtocol( u.protocol() );
+ getNetworkProtocol();
+ }
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpGet, u, QString(), QString() );
+ return startOperation( res );
+}
+
+/*!
+ This function tells the network protocol to put \a data in \a
+ location. If \a location is empty, it puts the \a data in the
+ location to which the URL points. What happens depends on
+ the network protocol. Depending on the network protocol, some
+ data might come back after putting data, in which case the data()
+ signal is emitted. The dataTransferProgress() signal is emitted
+ during processing of the operation. At the end, finished() (with
+ success or failure) is emitted, so check the state of the network
+ operation object to see whether or not the operation was
+ successful.
+
+ If \a location is empty, the path of this Q3UrlOperator should
+ point to a file when you use this operation. If \a location
+ is not empty, it can be a relative (a child of the path to which
+ the Q3UrlOperator points) or an absolute URL.
+
+ For putting some data to a file you can do the following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 4
+
+ For most other operations, the path of the Q3UrlOperator must point
+ to a directory. If you want to upload data to a file you could do
+ the following:
+
+ \snippet doc/src/snippets/code/src_qt3support_network_q3urloperator.cpp 5
+
+ This will upload the data to ftp://ftp.whatever.com/home/me/filename.dat.
+
+ \sa copy()
+*/
+
+const Q3NetworkOperation *Q3UrlOperator::put( const QByteArray &data, const QString &location )
+{
+ Q3Url u( *this );
+ if ( !location.isEmpty() )
+ u = Q3Url( *this, location );
+
+ if ( !u.isValid() )
+ return 0;
+
+ if ( !d->networkProtocol ) {
+ setProtocol( u.protocol() );
+ getNetworkProtocol();
+ }
+
+ Q3NetworkOperation *res = new Q3NetworkOperation( Q3NetworkProtocol::OpPut, u, QString(), QString() );
+ res->setRawArg( 1, data );
+ return startOperation( res );
+}
+
+/*!
+ Sets the name filter of the URL to \a nameFilter.
+
+ \sa QDir::setNameFilter()
+*/
+
+void Q3UrlOperator::setNameFilter( const QString &nameFilter )
+{
+ d->nameFilter = nameFilter;
+}
+
+/*!
+ Returns the name filter of the URL.
+
+ \sa Q3UrlOperator::setNameFilter() QDir::nameFilter()
+*/
+
+QString Q3UrlOperator::nameFilter() const
+{
+ return d->nameFilter;
+}
+
+/*!
+ Clears the cache of children.
+*/
+
+void Q3UrlOperator::clearEntries()
+{
+ d->entryMap.clear();
+}
+
+/*!
+ Adds an entry to the cache of children.
+*/
+
+void Q3UrlOperator::addEntry( const Q3ValueList<QUrlInfo> &i )
+{
+ Q3ValueList<QUrlInfo>::ConstIterator it = i.begin();
+ for ( ; it != i.end(); ++it )
+ d->entryMap[ ( *it ).name().stripWhiteSpace() ] = *it;
+}
+
+/*!
+ Returns the URL information for the child \a entry, or returns an
+ empty QUrlInfo object if there is no information available about
+ \a entry. Information about \a entry is only available after a successfully
+ finished listChildren() operation.
+*/
+
+QUrlInfo Q3UrlOperator::info( const QString &entry ) const
+{
+ if ( d->entryMap.contains( entry.stripWhiteSpace() ) ) {
+ return d->entryMap[ entry.stripWhiteSpace() ];
+ } else if ( entry == QLatin1String(".") || entry == QLatin1String("..") ) {
+ // return a faked QUrlInfo
+ QUrlInfo inf;
+ inf.setName( entry );
+ inf.setDir( true );
+ inf.setFile( false );
+ inf.setSymLink( false );
+ inf.setOwner( tr( "(unknown)" ) );
+ inf.setGroup( tr( "(unknown)" ) );
+ inf.setSize( 0 );
+ inf.setWritable( false );
+ inf.setReadable( true );
+ return inf;
+ }
+ return QUrlInfo();
+}
+
+/*!
+ Finds a network protocol for the URL and deletes the old network protocol.
+*/
+
+void Q3UrlOperator::getNetworkProtocol()
+{
+ delete d->networkProtocol;
+ Q3NetworkProtocol *p = Q3NetworkProtocol::getNetworkProtocol( protocol() );
+ if ( !p ) {
+ d->networkProtocol = 0;
+ return;
+ }
+
+ d->networkProtocol = (Q3NetworkProtocol *)p;
+ d->networkProtocol->setUrl( this );
+ connect( d->networkProtocol, SIGNAL(itemChanged(Q3NetworkOperation*)),
+ this, SLOT(slotItemChanged(Q3NetworkOperation*)) );
+}
+
+/*!
+ Deletes the currently used network protocol.
+*/
+
+void Q3UrlOperator::deleteNetworkProtocol()
+{
+ if (d->networkProtocol) {
+ d->networkProtocol->deleteLater();
+ d->networkProtocol = 0;
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void Q3UrlOperator::setPath( const QString& path )
+{
+ Q3Url::setPath( path );
+ if ( d->networkProtocol )
+ d->networkProtocol->setUrl( this );
+}
+
+/*!
+ \reimp
+*/
+
+void Q3UrlOperator::reset()
+{
+ Q3Url::reset();
+ deleteNetworkProtocol();
+ d->nameFilter = QLatin1String("*");
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3UrlOperator::parse( const QString &url )
+{
+ bool b = Q3Url::parse( url );
+ if ( !b ) {
+ return b;
+ }
+
+ getNetworkProtocol();
+
+ return b;
+}
+
+/*!
+ Assigns \a url to this object.
+*/
+
+Q3UrlOperator& Q3UrlOperator::operator=( const Q3UrlOperator &url )
+{
+ deleteNetworkProtocol();
+ Q3Url::operator=( url );
+
+ Q3PtrDict<Q3NetworkOperation> getOpPutOpMap = d->getOpPutOpMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpPutProtMap = d->getOpPutProtMap;
+ Q3PtrDict<Q3NetworkProtocol> getOpGetProtMap = d->getOpGetProtMap;
+ Q3PtrDict<Q3NetworkOperation> getOpRemoveOpMap = d->getOpRemoveOpMap;
+
+ *d = *url.d;
+
+ d->oldOps.setAutoDelete( false );
+ d->getOpPutOpMap = getOpPutOpMap;
+ d->getOpPutProtMap = getOpPutProtMap;
+ d->getOpGetProtMap = getOpGetProtMap;
+ d->getOpRemoveOpMap = getOpRemoveOpMap;
+
+ d->networkProtocol = 0;
+ getNetworkProtocol();
+ return *this;
+}
+
+/*!
+ Assigns \a url to this object.
+*/
+
+Q3UrlOperator& Q3UrlOperator::operator=( const QString &url )
+{
+ deleteNetworkProtocol();
+ Q3Url::operator=( url );
+ d->oldOps.setAutoDelete( false );
+ getNetworkProtocol();
+ return *this;
+}
+
+/*!
+ \internal
+*/
+
+bool Q3UrlOperator::cdUp()
+{
+ bool b = Q3Url::cdUp();
+ if ( d->networkProtocol )
+ d->networkProtocol->setUrl( this );
+ return b;
+}
+
+/*!
+ \internal
+*/
+
+bool Q3UrlOperator::checkValid()
+{
+ // ######
+ if ( !isValid() ) {
+ //emit error( ErrValid, tr( "The entered URL is not valid!" ) );
+ return false;
+ } else
+ return true;
+}
+
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::copyGotData( const QByteArray &data_, Q3NetworkOperation *op )
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: copyGotData: %d new bytes", data_.size() );
+#endif
+ Q3NetworkOperation *put = d->getOpPutOpMap[ (void*)op ];
+ if ( put ) {
+ QByteArray &s = put->raw( 1 );
+ int size = s.size();
+ s.resize( size + data_.size() );
+ memcpy( s.data() + size, data_.data(), data_.size() );
+ }
+ emit data( data_, op );
+}
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::continueCopy( Q3NetworkOperation *op )
+{
+ if ( op->operation() != Q3NetworkProtocol::OpGet )
+ return;
+ if ( op->state()!=Q3NetworkProtocol::StDone && op->state()!=Q3NetworkProtocol::StFailed ) {
+ return;
+ }
+
+#ifdef Q3URLOPERATOR_DEBUG
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ qDebug( "Q3UrlOperator: continue copy (get finished, put will start)" );
+ }
+#endif
+
+ Q3NetworkOperation *put = d->getOpPutOpMap[ (void*)op ];
+ Q3NetworkProtocol *gProt = d->getOpGetProtMap[ (void*)op ];
+ Q3NetworkProtocol *pProt = d->getOpPutProtMap[ (void*)op ];
+ Q3NetworkOperation *rm = d->getOpRemoveOpMap[ (void*)op ];
+ d->getOpPutOpMap.take( op );
+ d->getOpGetProtMap.take( op );
+ d->getOpPutProtMap.take( op );
+ d->getOpRemoveOpMap.take( op );
+ if ( pProt )
+ pProt->setAutoDelete( true );
+ if ( put && pProt ) {
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ pProt->addOperation( put );
+ d->currPut = pProt;
+ if (rm) { // we need the result of the put operation
+ qApp->processEvents(); // process posted operations
+ if (put->state() == Q3NetworkProtocol::StFailed) {
+ deleteOperation( rm );
+ rm = 0;
+ }
+ }
+ } else {
+ deleteOperation( put );
+ }
+ }
+ if ( gProt ) {
+ gProt->setAutoDelete( true );
+ }
+ if ( rm && gProt ) {
+ if ( op->state() != Q3NetworkProtocol::StFailed ) {
+ gProt->addOperation( rm );
+ } else {
+ deleteOperation( rm );
+ }
+ }
+ disconnect( gProt, SIGNAL(data(QByteArray,Q3NetworkOperation*)),
+ this, SLOT(copyGotData(QByteArray,Q3NetworkOperation*)) );
+ disconnect( gProt, SIGNAL(finished(Q3NetworkOperation*)),
+ this, SLOT(continueCopy(Q3NetworkOperation*)) );
+}
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::finishedCopy()
+{
+#ifdef Q3URLOPERATOR_DEBUG
+ qDebug( "Q3UrlOperator: finished copy (finished putting)" );
+#endif
+
+ if ( d->waitingCopies.isEmpty() )
+ return;
+
+ QString cp = d->waitingCopies.first();
+ d->waitingCopies.remove( cp );
+ Q3PtrList<Q3NetworkOperation> lst = copy( cp, d->waitingCopiesDest, d->waitingCopiesMove );
+ emit startedNextCopy( lst );
+}
+
+/*!
+ Stops the current network operation and removes all this
+ Q3UrlOperator's waiting network operations.
+*/
+
+void Q3UrlOperator::stop()
+{
+ d->getOpPutOpMap.clear();
+ d->getOpRemoveOpMap.clear();
+ d->getOpGetProtMap.setAutoDelete( true );
+ d->getOpPutProtMap.setAutoDelete( true );
+ Q3PtrDictIterator<Q3NetworkProtocol> it( d->getOpPutProtMap );
+ for ( ; it.current(); ++it )
+ it.current()->stop();
+ d->getOpPutProtMap.clear();
+ it = Q3PtrDictIterator<Q3NetworkProtocol>( d->getOpGetProtMap );
+ for ( ; it.current(); ++it )
+ it.current()->stop();
+ d->getOpGetProtMap.clear();
+ if ( d->currPut ) {
+ d->currPut->stop();
+ delete (Q3NetworkProtocol *) d->currPut;
+ d->currPut = 0;
+ }
+ d->waitingCopies.clear();
+ if ( d->networkProtocol )
+ d->networkProtocol->stop();
+ getNetworkProtocol();
+}
+
+/*!
+ \internal
+*/
+
+void Q3UrlOperator::deleteOperation( Q3NetworkOperation *op )
+{
+ if ( op )
+ d->oldOps.append( op );
+}
+
+/*!
+ \internal
+ updates the entryMap after a network operation finished
+*/
+
+void Q3UrlOperator::slotItemChanged( Q3NetworkOperation *op )
+{
+ if ( !op )
+ return;
+
+ switch ( op->operation() ) {
+ case Q3NetworkProtocol::OpRename :
+ {
+ if ( op->arg( 0 ) == op->arg( 1 ) )
+ return;
+
+ QMap<QString, QUrlInfo>::iterator mi = d->entryMap.find( op->arg( 0 ) );
+ if ( mi != d->entryMap.end() ) {
+ mi.data().setName( op->arg( 1 ) );
+ d->entryMap[ op->arg( 1 ) ] = mi.data();
+ d->entryMap.erase( mi );
+ }
+ break;
+ }
+ case Q3NetworkProtocol::OpRemove :
+ {
+ QMap<QString, QUrlInfo>::iterator mi = d->entryMap.find( op->arg( 0 ) );
+ if ( mi != d->entryMap.end() )
+ d->entryMap.erase( mi );
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_q3urloperator.cpp"
+
+#endif // QT_NO_NETWORKPROTOCOL
diff --git a/src/qt3support/network/q3urloperator.h b/src/qt3support/network/q3urloperator.h
new file mode 100644
index 0000000000..071e506971
--- /dev/null
+++ b/src/qt3support/network/q3urloperator.h
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3URLOPERATOR_H
+#define Q3URLOPERATOR_H
+
+#include <QtCore/qobject.h>
+#include <Qt3Support/q3url.h>
+#include <Qt3Support/q3ptrlist.h>
+#include <Qt3Support/q3networkprotocol.h>
+#include <QtCore/qstringlist.h> // QString->QStringList conversion
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_NETWORKPROTOCOL
+
+class QUrlInfo;
+class Q3UrlOperatorPrivate;
+class Q3NetworkProtocol;
+
+class Q_COMPAT_EXPORT Q3UrlOperator : public QObject, public Q3Url
+{
+ Q_OBJECT
+ friend class Q3NetworkProtocol;
+
+public:
+ Q3UrlOperator();
+ Q3UrlOperator( const QString &urL );
+ Q3UrlOperator( const Q3UrlOperator& url );
+ Q3UrlOperator( const Q3UrlOperator& url, const QString& relUrl, bool checkSlash = false );
+ virtual ~Q3UrlOperator();
+
+ virtual void setPath( const QString& path );
+ virtual bool cdUp();
+
+ virtual const Q3NetworkOperation *listChildren();
+ virtual const Q3NetworkOperation *mkdir( const QString &dirname );
+ virtual const Q3NetworkOperation *remove( const QString &filename );
+ virtual const Q3NetworkOperation *rename( const QString &oldname, const QString &newname );
+ virtual const Q3NetworkOperation *get( const QString &location = QString() );
+ virtual const Q3NetworkOperation *put( const QByteArray &data, const QString &location = QString() );
+ virtual Q3PtrList<Q3NetworkOperation> copy( const QString &from, const QString &to, bool move = false, bool toPath = true );
+ virtual void copy( const QStringList &files, const QString &dest, bool move = false );
+ virtual bool isDir( bool *ok = 0 );
+
+ virtual void setNameFilter( const QString &nameFilter );
+ QString nameFilter() const;
+
+ virtual QUrlInfo info( const QString &entry ) const;
+
+ Q3UrlOperator& operator=( const Q3UrlOperator &url );
+ Q3UrlOperator& operator=( const QString &url );
+
+ virtual void stop();
+
+Q_SIGNALS:
+ void newChildren( const Q3ValueList<QUrlInfo> &, Q3NetworkOperation *res );
+ void finished( Q3NetworkOperation *res );
+ void start( Q3NetworkOperation *res );
+ void createdDirectory( const QUrlInfo &, Q3NetworkOperation *res );
+ void removed( Q3NetworkOperation *res );
+ void itemChanged( Q3NetworkOperation *res );
+ void data( const QByteArray &, Q3NetworkOperation *res );
+ void dataTransferProgress( int bytesDone, int bytesTotal, Q3NetworkOperation *res );
+ void startedNextCopy( const Q3PtrList<Q3NetworkOperation> &lst );
+ void connectionStateChanged( int state, const QString &data );
+
+protected:
+ void reset();
+ bool parse( const QString& url );
+ virtual bool checkValid();
+ virtual void clearEntries();
+ void getNetworkProtocol();
+ void deleteNetworkProtocol();
+
+private Q_SLOTS:
+ const Q3NetworkOperation *startOperation( Q3NetworkOperation *op );
+ void copyGotData( const QByteArray &data, Q3NetworkOperation *op );
+ void continueCopy( Q3NetworkOperation *op );
+ void finishedCopy();
+ void addEntry( const Q3ValueList<QUrlInfo> &i );
+ void slotItemChanged( Q3NetworkOperation *op );
+
+private:
+ void deleteOperation( Q3NetworkOperation *op );
+
+ Q3UrlOperatorPrivate *d;
+};
+
+#endif // QT_NO_NETWORKPROTOCOL
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3URLOPERATOR_H