summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-11-08 12:31:36 -0600
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-11-08 12:31:36 -0600
commitd796c9dd933ab96ec83b9a634feedd5d32e1ba3f (patch)
tree6e3dcca4f77e20ec8966c666aac7c35bd4704053 /src/network
downloadtqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.tar.gz
tqt3-d796c9dd933ab96ec83b9a634feedd5d32e1ba3f.zip
Test conversion to TQt3 from Qt3 8c6fc1f8e35fd264dd01c582ca5e7549b32ab731
Diffstat (limited to 'src/network')
-rw-r--r--src/network/qdns.cpp2692
-rw-r--r--src/network/qdns.h170
-rw-r--r--src/network/qftp.cpp2419
-rw-r--r--src/network/qftp.h202
-rw-r--r--src/network/qhostaddress.cpp453
-rw-r--r--src/network/qhostaddress.h101
-rw-r--r--src/network/qhttp.cpp2384
-rw-r--r--src/network/qhttp.h277
-rw-r--r--src/network/qnetwork.cpp71
-rw-r--r--src/network/qnetwork.h60
-rw-r--r--src/network/qserversocket.cpp297
-rw-r--r--src/network/qserversocket.h94
-rw-r--r--src/network/qsocket.cpp1546
-rw-r--r--src/network/qsocket.h156
-rw-r--r--src/network/qsocketdevice.cpp576
-rw-r--r--src/network/qsocketdevice.h170
-rw-r--r--src/network/qsocketdevice_unix.cpp1099
-rw-r--r--src/network/qt_network.pri23
18 files changed, 12790 insertions, 0 deletions
diff --git a/src/network/qdns.cpp b/src/network/qdns.cpp
new file mode 100644
index 00000000..65deec7f
--- /dev/null
+++ b/src/network/qdns.cpp
@@ -0,0 +1,2692 @@
+/****************************************************************************
+**
+** Implementation of TQDns class.
+**
+** Created : 991122
+**
+** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qplatformdefs.h"
+
+// 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 "qdns.h"
+
+#ifndef QT_NO_DNS
+
+#include "qdatetime.h"
+#include "qdict.h"
+#include "qptrlist.h"
+#include "qstring.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qptrvector.h"
+#include "qstrlist.h"
+#include "qptrdict.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "qsocketdevice.h"
+#include "qcleanuphandler.h"
+#include <limits.h>
+#ifdef Q_OS_MAC
+#include "../3rdparty/dlcompat/dlfcn.h"
+#endif
+
+//#define TQDNS_DEBUG
+
+static Q_UINT16 id; // ### seeded started by now()
+
+
+static TQDateTime * originOfTime = 0;
+
+static TQCleanupHandler<TQDateTime> qdns_cleanup_time;
+
+static Q_UINT32 now()
+{
+ if ( originOfTime )
+ return originOfTime->secsTo( TQDateTime::currentDateTime() );
+
+ originOfTime = new TQDateTime( TQDateTime::currentDateTime() );
+ ::id = originOfTime->time().msec() * 60 + originOfTime->time().second();
+ qdns_cleanup_time.add( &originOfTime );
+ return 0;
+}
+
+
+static TQPtrList<TQHostAddress> * ns = 0;
+static TQStrList * domains = 0;
+static bool ipv6support = FALSE;
+
+static int qdns_res_init()
+{
+#ifdef Q_OS_MAC
+ typedef int (*PtrRes_init)();
+ static PtrRes_init ptrRes_init = 0;
+ if (!ptrRes_init)
+ ptrRes_init = (PtrRes_init)DL_PREFIX(dlsym)(RTLD_NEXT, "res_init");
+ if (ptrRes_init)
+ return (*ptrRes_init)();
+ else
+ return -1;
+#elif defined(Q_OS_UNIX)
+ return res_init();
+#else
+ return 0; // not called at all on Windows.
+#endif
+}
+
+
+class TQDnsPrivate {
+public:
+ TQDnsPrivate() : 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
+ }
+ ~TQDnsPrivate()
+ {
+ delete queryTimer;
+ }
+private:
+ TQTimer * queryTimer;
+ bool noNames;
+#if defined(Q_DNS_SYNCHRONOUS)
+ bool noEventLoop;
+#endif
+
+ friend class TQDns;
+ friend class TQDnsAnswer;
+};
+
+
+class TQDnsRR;
+class TQDnsDomain;
+
+
+
+// TQDnsRR is the class used to store a single RR. TQDnsRR can store
+// all of the supported RR types. a TQDnsRR is always cached.
+
+// TQDnsRR is mostly constructed from the outside. a but hacky, but
+// permissible since the entire class is internal.
+
+class TQDnsRR {
+public:
+ TQDnsRR( const TQString & label );
+ ~TQDnsRR();
+
+public:
+ TQDnsDomain * domain;
+ TQDns::RecordType t;
+ bool nxdomain;
+ bool current;
+ Q_UINT32 expireTime;
+ Q_UINT32 deleteTime;
+ // somewhat space-wasting per-type data
+ // a / aaaa
+ TQHostAddress address;
+ // cname / mx / srv / ptr
+ TQString target;
+ // mx / srv
+ Q_UINT16 priority;
+ // srv
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ // txt
+ TQString text; // could be overloaded into target...
+private:
+
+};
+
+
+class TQDnsDomain {
+public:
+ TQDnsDomain( const TQString & label );
+ ~TQDnsDomain();
+
+ static void add( const TQString & label, TQDnsRR * );
+ static TQPtrList<TQDnsRR> * cached( const TQDns * );
+
+ void take( TQDnsRR * );
+
+ void sweep( Q_UINT32 thisSweep );
+
+ bool isEmpty() const { return rrs == 0 || rrs->isEmpty(); }
+
+ TQString name() const { return l; }
+
+public:
+ TQString l;
+ TQPtrList<TQDnsRR> * rrs;
+};
+
+
+class TQDnsQuery: public TQTimer { // this inheritance is a very evil hack
+public:
+ TQDnsQuery():
+ id( 0 ), t( TQDns::None ), step(0), started(0),
+ dns( new TQPtrDict<void>(17) ) {}
+ ~TQDnsQuery() { delete dns; }
+ Q_UINT16 id;
+ TQDns::RecordType t;
+ TQString l;
+
+ uint step;
+ Q_UINT32 started;
+
+ TQPtrDict<void> * dns;
+};
+
+
+
+class TQDnsAnswer {
+public:
+ TQDnsAnswer( TQDnsQuery * );
+ TQDnsAnswer( const TQByteArray &, TQDnsQuery * );
+ ~TQDnsAnswer();
+
+ void parse();
+ void notify();
+
+ bool ok;
+
+private:
+ TQDnsQuery * query;
+
+ Q_UINT8 * answer;
+ int size;
+ int pp;
+
+ TQPtrList<TQDnsRR> * rrs;
+
+ // convenience
+ int next;
+ int ttl;
+ TQString label;
+ TQDnsRR * rr;
+
+ TQString readString(bool multipleLabels = TRUE);
+ void parseA();
+ void parseAaaa();
+ void parseMx();
+ void parseSrv();
+ void parseCname();
+ void parsePtr();
+ void parseTxt();
+ void parseNs();
+};
+
+
+TQDnsRR::TQDnsRR( const TQString & label )
+ : domain( 0 ), t( TQDns::None ),
+ nxdomain( FALSE ), current( FALSE ),
+ expireTime( 0 ), deleteTime( 0 ),
+ priority( 0 ), weight( 0 ), port( 0 )
+{
+ TQDnsDomain::add( label, this );
+}
+
+
+// not supposed to be deleted except by TQDnsDomain
+TQDnsRR::~TQDnsRR()
+{
+ // nothing is necessary
+}
+
+
+// this one just sticks in a NXDomain
+TQDnsAnswer::TQDnsAnswer( TQDnsQuery * query_ )
+{
+ ok = TRUE;
+
+ answer = 0;
+ size = 0;
+ query = query_;
+ pp = 0;
+ rrs = new TQPtrList<TQDnsRR>;
+ rrs->setAutoDelete( FALSE );
+ next = size;
+ ttl = 0;
+ label = TQString::null;
+ rr = 0;
+
+ TQDnsRR * newrr = new TQDnsRR( 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 );
+}
+
+
+TQDnsAnswer::TQDnsAnswer( const TQByteArray& answer_,
+ TQDnsQuery * query_ )
+{
+ ok = TRUE;
+
+ answer = (Q_UINT8 *)(answer_.data());
+ size = (int)answer_.size();
+ query = query_;
+ pp = 0;
+ rrs = new TQPtrList<TQDnsRR>;
+ rrs->setAutoDelete( FALSE );
+ next = size;
+ ttl = 0;
+ label = TQString::null;
+ rr = 0;
+}
+
+
+TQDnsAnswer::~TQDnsAnswer()
+{
+ if ( !ok && rrs ) {
+ TQPtrListIterator<TQDnsRR> it( *rrs );
+ TQDnsRR * tmprr;
+ while( (tmprr=it.current()) != 0 ) {
+ ++it;
+ tmprr->t = TQDns::None; // will be deleted soonish
+ }
+ }
+ delete rrs;
+}
+
+
+TQString TQDnsAnswer::readString(bool multipleLabels)
+{
+ int p = pp;
+ TQString r = TQString::null;
+ 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() ? TQString( "." ) : r;
+ }
+
+ // Read a label of size 'b' characters
+ if ( !r.isNull() )
+ r += '.';
+ while( b-- > 0 ) {
+ r += TQChar( answer[p] );
+ 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 TQString::null;
+}
+
+
+
+void TQDnsAnswer::parseA()
+{
+ if ( next != pp + 4 ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %d bytes long IN A for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( label );
+ rr->t = TQDns::A;
+ rr->address = TQHostAddress( ( answer[pp+0] << 24 ) +
+ ( answer[pp+1] << 16 ) +
+ ( answer[pp+2] << 8 ) +
+ ( answer[pp+3] ) );
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN A %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parseAaaa()
+{
+ if ( next != pp + 16 ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %d bytes long IN Aaaa for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( label );
+ rr->t = TQDns::Aaaa;
+ rr->address = TQHostAddress( answer+pp );
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN Aaaa %s (ttl %d)", label.ascii(),
+ rr->address.toString().ascii(), ttl );
+#endif
+}
+
+
+
+void TQDnsAnswer::parseMx()
+{
+ if ( next < pp + 2 ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %d bytes long IN MX for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( label );
+ rr->priority = (answer[pp] << 8) + answer[pp+1];
+ pp += 2;
+ rr->target = readString().lower();
+ if ( !ok ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw bad string in MX for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = TQDns::Mx;
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN MX %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parseSrv()
+{
+ if ( next < pp + 6 ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %d bytes long IN SRV for %s",
+ next - pp, label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( 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(TQDNS_DEBUG)
+ qDebug( "TQDns: saw bad string in SRV for %s", label.ascii() );
+#endif
+ return;
+ }
+ rr->t = TQDns::Srv;
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN SRV %d %d %d %s (ttl %d)", label.ascii(),
+ rr->priority, rr->weight, rr->port, rr->target.ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parseCname()
+{
+ TQString target = readString().lower();
+ if ( !ok ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( label );
+ rr->t = TQDns::Cname;
+ rr->target = target;
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN CNAME %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parseNs()
+{
+ TQString target = readString().lower();
+ if ( !ok ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw bad cname for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ // parse, but ignore
+
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN NS %s (ttl %d)", label.ascii(),
+ target.ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parsePtr()
+{
+ TQString target = readString().lower();
+ if ( !ok ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw bad PTR for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( label );
+ rr->t = TQDns::Ptr;
+ rr->target = target;
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN PTR %s (ttl %d)", label.ascii(),
+ rr->target.ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parseTxt()
+{
+ TQString text = readString(FALSE);
+ if ( !ok ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw bad TXT for for %s", label.ascii() );
+#endif
+ return;
+ }
+
+ rr = new TQDnsRR( label );
+ rr->t = TQDns::Txt;
+ rr->text = text;
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns: saw %s IN TXT \"%s\" (ttl %d)", label.ascii(),
+ rr->text.ascii(), ttl );
+#endif
+}
+
+
+void TQDnsAnswer::parse()
+{
+ // okay, do the work...
+ if ( (answer[2] & 0x78) != 0 ) {
+#if defined(TQDNS_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(TQDNS_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(TQDNS_DEBUG)
+ qDebug( "DNS Manager: saw NXDomain for %s", query->l.ascii() );
+#endif
+ // NXDomain. cache that for one minute.
+ rr = new TQDnsRR( 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(TQDNS_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(TQDNS_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(TQDNS_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(TQDNS_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(TQDNS_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();
+ TQDict<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 == TQDns::A || rr->t == TQDns::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 ) {
+ TQPtrList<TQDnsRR> * drrs = rr->domain->rrs;
+ drrs->first();
+ TQDnsRR * 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(TQDNS_DEBUG)
+ qDebug( "killing off old %d for %s, expire was %d",
+ older->t, older->domain->name().latin1(),
+ rr->expireTime );
+#endif
+ older->t = TQDns::None;
+ rr->expireTime = TQMAX( older->expireTime, rr->expireTime );
+ rr->deleteTime = TQMAX( older->deleteTime, rr->deleteTime );
+ older->deleteTime = 0;
+#if defined(TQDNS_DEBUG)
+ qDebug( " adjusted expire is %d", rr->expireTime );
+#endif
+ }
+ drrs->next();
+ }
+ }
+ }
+
+#if defined(TQDNS_DEBUG)
+ //qDebug( "DNS Manager: ()" );
+#endif
+}
+
+
+class TQDnsUgleHack: public TQDns {
+public:
+ void ugle( bool emitAnyway=FALSE );
+};
+
+
+void TQDnsAnswer::notify()
+{
+ if ( !rrs || !ok || !query || !query->dns )
+ return;
+
+ TQPtrDict<void> notified;
+ notified.setAutoDelete( FALSE );
+
+ TQPtrDictIterator<void> it( *query->dns );
+ TQDns * dns;
+ it.toFirst();
+ while( (dns=(TQDns*)(it.current())) != 0 ) {
+ ++it;
+ if ( notified.find( (void*)dns ) == 0 ) {
+ notified.insert( (void*)dns, (void*)42 );
+ if ( rrs->count() == 0 ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "DNS Manager: found no answers!" );
+#endif
+ dns->d->noNames = TRUE;
+ ((TQDnsUgleHack*)dns)->ugle( TRUE );
+ } else {
+ TQStringList n = dns->qualifiedNames();
+ if ( query && n.contains(query->l) )
+ ((TQDnsUgleHack*)dns)->ugle();
+#if defined(TQDNS_DEBUG)
+ else
+ qDebug( "DNS Manager: DNS thing %s not notified for %s",
+ dns->label().ascii(), query->l.ascii() );
+#endif
+ }
+ }
+ }
+}
+
+
+//
+//
+// TQDnsManager
+//
+//
+
+
+class TQDnsManager: public TQDnsSocket {
+private:
+public: // just to silence the moronic g++.
+ TQDnsManager();
+ ~TQDnsManager();
+public:
+ static TQDnsManager * manager();
+
+ TQDnsDomain * domain( const TQString & );
+
+ void transmitQuery( TQDnsQuery * );
+ void transmitQuery( int );
+
+ // reimplementation of the slots
+ void cleanCache();
+ void retransmit();
+ void answer();
+
+public:
+ TQPtrVector<TQDnsQuery> queries;
+ TQDict<TQDnsDomain> cache;
+ TQSocketDevice * ipv4Socket;
+#if !defined (QT_NO_IPV6)
+ TQSocketDevice * ipv6Socket;
+#endif
+};
+
+
+
+static TQDnsManager * globalManager = 0;
+
+static void cleanupDns()
+{
+ delete globalManager;
+ globalManager = 0;
+}
+
+TQDnsManager * TQDnsManager::manager()
+{
+ if ( !globalManager ) {
+ qAddPostRoutine(cleanupDns);
+ new TQDnsManager();
+ }
+ return globalManager;
+}
+
+
+void TQDnsUgleHack::ugle( bool emitAnyway)
+{
+ if ( emitAnyway || !isWorking() ) {
+#if defined(TQDNS_DEBUG)
+ qDebug( "DNS Manager: status change for %s (type %d)",
+ label().ascii(), recordType() );
+#endif
+ emit resultsReady();
+ }
+}
+
+
+TQDnsManager::TQDnsManager()
+ : TQDnsSocket( qApp, "Internal DNS manager" ),
+ queries( TQPtrVector<TQDnsQuery>( 0 ) ),
+ cache( TQDict<TQDnsDomain>( 83, FALSE ) ),
+ ipv4Socket( new TQSocketDevice( TQSocketDevice::Datagram, TQSocketDevice::IPv4, 0 ) )
+#if !defined (QT_NO_IPV6)
+ , ipv6Socket( new TQSocketDevice( TQSocketDevice::Datagram, TQSocketDevice::IPv6, 0 ) )
+#endif
+{
+ cache.setAutoDelete( TRUE );
+ globalManager = this;
+
+ TQTimer * sweepTimer = new TQTimer( this );
+ sweepTimer->start( 1000 * 60 * 3 );
+ connect( sweepTimer, SIGNAL(timeout()),
+ this, SLOT(cleanCache()) );
+
+ TQSocketNotifier * rn4 = new TQSocketNotifier( ipv4Socket->socket(),
+ TQSocketNotifier::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 ) {
+ TQSocketNotifier * rn6 = new TQSocketNotifier( ipv6Socket->socket(),
+ TQSocketNotifier::Read,
+ this, "dns IPv6 socket watcher" );
+
+ ipv6support = TRUE;
+ ipv6Socket->setAddressReusable( FALSE );
+ ipv6Socket->setBlocking( FALSE );
+ connect( rn6, SIGNAL(activated(int)), SLOT(answer()) );
+ }
+#endif
+
+ if ( !ns )
+ TQDns::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.
+ TQPtrList<TQHostAddress> * ns = new TQPtrList<TQHostAddress>;
+
+ ::ns->first();
+ TQHostAddress * h;
+ while( (h=::ns->current()) != 0 ) {
+ ns->first();
+ while( ns->current() != 0 && !(*ns->current() == *h) )
+ ns->next();
+ if ( !ns->current() ) {
+ ns->append( new TQHostAddress(*h) );
+#if defined(TQDNS_DEBUG)
+ qDebug( "using name server %s", h->toString().latin1() );
+ } else {
+ qDebug( "skipping address %s", h->toString().latin1() );
+#endif
+ }
+ ::ns->next();
+ }
+
+ delete ::ns;
+ ::ns = ns;
+ ::ns->setAutoDelete( TRUE );
+
+ TQStrList * domains = new TQStrList( TRUE );
+
+ ::domains->first();
+ const char * s;
+ while( (s=::domains->current()) != 0 ) {
+ domains->first();
+ while( domains->current() != 0 && qstrcmp( domains->current(), s ) )
+ domains->next();
+ if ( !domains->current() ) {
+ domains->append( s );
+#if defined(TQDNS_DEBUG)
+ qDebug( "searching domain %s", s );
+ } else {
+ qDebug( "skipping domain %s", s );
+#endif
+ }
+ ::domains->next();
+ }
+
+ delete ::domains;
+ ::domains = domains;
+ ::domains->setAutoDelete( TRUE );
+}
+
+
+TQDnsManager::~TQDnsManager()
+{
+ 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 TQDnsManager::cleanCache()
+{
+ bool again = FALSE;
+ TQDictIterator<TQDnsDomain> it( cache );
+ TQDnsDomain * d;
+ Q_UINT32 thisSweep = now();
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDnsManager::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 TQDnsManager::retransmit()
+{
+ const TQObject * 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 TQDnsManager::answer()
+{
+ TQByteArray a( 16383 ); // large enough for anything, one suspects
+
+ int r;
+#if defined (QT_NO_IPV6)
+ r = ipv4Socket->readBlock(a.data(), a.size());
+#else
+ if (((TQSocketNotifier *)sender())->socket() == ipv4Socket->socket())
+ r = ipv4Socket->readBlock(a.data(), a.size());
+ else
+ r = ipv6Socket->readBlock(a.data(), a.size());
+#endif
+#if defined(TQDNS_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(TQDNS_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(TQDNS_DEBUG)
+ qDebug( "DNS Manager: received a query" );
+#endif
+ return;
+ }
+
+ TQDnsQuery * q = queries[i];
+ TQDnsAnswer answer( a, q );
+ answer.parse();
+ if ( answer.ok ) {
+ queries.take( i );
+ answer.notify();
+ delete q;
+ }
+}
+
+
+void TQDnsManager::transmitQuery( TQDnsQuery * 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 TQDnsManager::transmitQuery( int i )
+{
+ if ( i < 0 || i >= (int)queries.size() )
+ return;
+ TQDnsQuery * 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...
+ TQDnsAnswer answer( q );
+ answer.notify();
+
+ if (globalManager == 0)
+ return;
+
+ // and then get rid of the query
+ queries.take( i );
+#if defined(TQDNS_DEBUG)
+ qDebug( "DNS Manager: giving up on query 0x%04x", q->id );
+#endif
+ delete q;
+ TQTimer::singleShot( 0, TQDnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ if ( q && !q->dns || q->dns->isEmpty() )
+ // noone 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;
+
+ TQByteArray 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 < q->l.length() ) {
+ int le = q->l.find( '.', lp );
+ if ( le < 0 )
+ le = q->l.length();
+ TQString 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 TQDns::A:
+ p[pp++] = 1;
+ break;
+ case TQDns::Aaaa:
+ p[pp++] = 28;
+ break;
+ case TQDns::Mx:
+ p[pp++] = 15;
+ break;
+ case TQDns::Srv:
+ p[pp++] = 33;
+ break;
+ case TQDns::Cname:
+ p[pp++] = 5;
+ break;
+ case TQDns::Ptr:
+ p[pp++] = 12;
+ break;
+ case TQDns::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 ( !ns || ns->isEmpty() )
+ TQDns::doResInit();
+
+ if ( !ns || ns->isEmpty() ) {
+ // we don't find any name servers. We fake an NXDomain
+ // with a very short life time...
+ TQDnsAnswer answer( q );
+ answer.notify();
+ // and then get rid of the query
+ queries.take( i );
+#if defined(TQDNS_DEBUG)
+ qDebug( "DNS Manager: no DNS server found on query 0x%04x", q->id );
+#endif
+ delete q;
+ TQTimer::singleShot( 1000*10, TQDnsManager::manager(), SLOT(cleanCache()) );
+ // and don't process anything more
+ return;
+ }
+
+ TQHostAddress receiver = *ns->at( q->step % ns->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(TQDNS_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 ( ns->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;
+ TQHostAddress * server;
+ while( (server=ns->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(TQDNS_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 < ns->count() ? 800 : 1500, TRUE );
+}
+
+
+TQDnsDomain * TQDnsManager::domain( const TQString & label )
+{
+ TQDnsDomain * d = cache.find( label );
+ if ( !d ) {
+ d = new TQDnsDomain( label );
+ cache.insert( label, d );
+ }
+ return d;
+}
+
+
+//
+//
+// the TQDnsDomain class looks after and coordinates queries for TQDnsRRs for
+// each domain, and the cached TQDnsRRs. (A domain, in DNS terminology, is
+// a node in the DNS. "no", "trolltech.com" and "lupinella.troll.no" are
+// all domains.)
+//
+//
+
+
+// this is ONLY to be called by TQDnsManager::domain(). noone else.
+TQDnsDomain::TQDnsDomain( const TQString & label )
+{
+ l = label;
+ rrs = 0;
+}
+
+
+TQDnsDomain::~TQDnsDomain()
+{
+ delete rrs;
+ rrs = 0;
+}
+
+
+void TQDnsDomain::add( const TQString & label, TQDnsRR * rr )
+{
+ TQDnsDomain * d = TQDnsManager::manager()->domain( label );
+ if ( !d->rrs ) {
+ d->rrs = new TQPtrList<TQDnsRR>;
+ d->rrs->setAutoDelete( TRUE );
+ }
+ d->rrs->append( rr );
+ rr->domain = d;
+}
+
+
+TQPtrList<TQDnsRR> * TQDnsDomain::cached( const TQDns * r )
+{
+ TQPtrList<TQDnsRR> * l = new TQPtrList<TQDnsRR>;
+
+ // test at first if you have to start a query at all
+ if ( r->recordType() == TQDns::A ) {
+ if ( r->label().lower() == "localhost" ) {
+ // undocumented hack. ipv4-specific. also, may be a memory
+ // leak? not sure. would be better to do this in doResInit(),
+ // anyway.
+ TQDnsRR *rrTmp = new TQDnsRR( r->label() );
+ rrTmp->t = TQDns::A;
+ rrTmp->address = TQHostAddress( 0x7f000001 );
+ rrTmp->current = TRUE;
+ l->append( rrTmp );
+ return l;
+ }
+ TQHostAddress tmp;
+ if ( tmp.setAddress( r->label() ) ) {
+ TQDnsRR *rrTmp = new TQDnsRR( r->label() );
+ if ( tmp.isIPv4Address() ) {
+ rrTmp->t = TQDns::A;
+ rrTmp->address = tmp;
+ rrTmp->current = TRUE;
+ l->append( rrTmp );
+ } else {
+ rrTmp->nxdomain = TRUE;
+ }
+ return l;
+ }
+ }
+ if ( r->recordType() == TQDns::Aaaa ) {
+ TQHostAddress tmp;
+ if ( tmp.setAddress(r->label()) ) {
+ TQDnsRR *rrTmp = new TQDnsRR( r->label() );
+ if ( tmp.isIPv6Address() ) {
+ rrTmp->t = TQDns::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
+ TQDnsManager * m = TQDnsManager::manager();
+ TQStringList n = r->qualifiedNames();
+ TQValueListIterator<TQString> it = n.begin();
+ TQValueListIterator<TQString> end = n.end();
+ bool nxdomain;
+ int cnamecount = 0;
+ while( it != end ) {
+ TQString s = *it++;
+ nxdomain = FALSE;
+#if defined(TQDNS_DEBUG)
+ qDebug( "looking at cache for %s (%s %d)",
+ s.ascii(), r->label().ascii(), r->recordType() );
+#endif
+ TQDnsDomain * d = m->domain( s );
+#if defined(TQDNS_DEBUG)
+ qDebug( " - found %d RRs", d && d->rrs ? d->rrs->count() : 0 );
+#endif
+ if ( d->rrs )
+ d->rrs->first();
+ TQDnsRR * rr;
+ bool answer = FALSE;
+ while( d->rrs && (rr=d->rrs->current()) != 0 ) {
+ if ( rr->t == TQDns::Cname && r->recordType() != TQDns::Cname &&
+ !rr->nxdomain && cnamecount < 16 ) {
+ // cname. if the code is ugly, that may just
+ // possibly be because the concept is.
+#if defined(TQDNS_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 = end;
+ // 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.
+ TQDnsQuery * query = new TQDnsQuery;
+ query->started = now();
+ query->id = ++::id;
+ 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 = ns->count();
+ TQObject::connect( query, SIGNAL(timeout()),
+ TQDnsManager::manager(),
+ SLOT(retransmit()) );
+ TQDnsManager::manager()->transmitQuery( query );
+ }
+ }
+ d->rrs->next();
+ }
+ }
+ // if we found a positive result, return tquickly
+ if ( answer && l->count() ) {
+#if defined(TQDNS_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(TQDNS_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( '.' ) >= 0 ||
+ l->count() >= n.count()-1 ) ) {
+ TQDnsQuery * query = new TQDnsQuery;
+ query->started = now();
+ query->id = ++::id;
+ query->t = r->recordType();
+ query->l = s;
+ query->dns->replace( (void*)r, (void*)r );
+ TQObject::connect( query, SIGNAL(timeout()),
+ TQDnsManager::manager(), SLOT(retransmit()) );
+ TQDnsManager::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 TQDnsDomain::sweep( Q_UINT32 thisSweep )
+{
+ if ( !rrs )
+ return;
+
+ TQDnsRR * rr;
+ rrs->first();
+ while( (rr=rrs->current()) != 0 ) {
+ if ( !rr->deleteTime )
+ rr->deleteTime = thisSweep; // will hit next time around
+
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::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 == TQDns::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.
+
+
+TQDnsSocket::TQDnsSocket( TQObject * parent, const char * name )
+ : TQObject( parent, name )
+{
+ // nothing
+}
+
+
+TQDnsSocket::~TQDnsSocket()
+{
+ // nothing
+}
+
+
+void TQDnsSocket::cleanCache()
+{
+ // nothing
+}
+
+
+void TQDnsSocket::retransmit()
+{
+ // nothing
+}
+
+
+void TQDnsSocket::answer()
+{
+ // nothing
+}
+
+
+/*!
+ \class TQDns qdns.h
+ \brief The TQDns class provides asynchronous DNS lookups.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \module network
+ \ingroup io
+
+ 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.
+
+ TQDns 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); TQSocket provides much easier to use
+ TCP connection facilities. The aim of TQDns 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 TQDns 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 TQSocket
+*/
+
+/*!
+ Constructs a DNS query object with invalid settings for both the
+ label and the search type.
+*/
+
+TQDns::TQDns()
+{
+ d = new TQDnsPrivate;
+ 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.
+*/
+
+TQDns::TQDns( const TQString & label, RecordType rr )
+{
+ d = new TQDnsPrivate;
+ 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.
+*/
+
+TQDns::TQDns( const TQHostAddress & address, RecordType rr )
+{
+ d = new TQDnsPrivate;
+ t = rr;
+ setLabel( address );
+ setStartQueryTimer(); // start query the next time we enter event loop
+}
+
+
+
+
+/*!
+ Destroys the DNS query object and frees its allocated resources.
+*/
+
+TQDns::~TQDns()
+{
+ if ( globalManager ) {
+ uint q = 0;
+ TQDnsManager * m = globalManager;
+ while( q < m->queries.size() ) {
+ TQDnsQuery * 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 TQDns::setLabel( const TQString & label )
+{
+ l = label;
+ d->noNames = FALSE;
+
+ // construct a list of qualified names
+ n.clear();
+ if ( l.length() > 1 && l[(int)l.length()-1] == '.' ) {
+ 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] == '.' )
+ dots++;
+ }
+ if ( dots < maxDots ) {
+ (void)TQDnsManager::manager(); // create a TQDnsManager, if it is not already there
+ TQStrListIterator it( *domains );
+ const char * dom;
+ while( (dom=it.current()) != 0 ) {
+ ++it;
+ n.append( l.lower() + "." + 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(TQDNS_DEBUG)
+ qDebug( "TQDns::setLabel: %d address(es) for %s", n.count(), l.ascii() );
+ int i = 0;
+ for( i = 0; i < (int)n.count(); i++ )
+ qDebug( "TQDns::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 TQDns::setLabel( const TQHostAddress & address )
+{
+ setLabel( toInAddrArpaDomain( address ) );
+}
+
+
+/*!
+ \fn TQStringList TQDns::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.
+ \code
+ TQStringList list = myDns.qualifiedNames();
+ TQStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+*/
+
+
+/*!
+ \fn TQString TQDns::label() const
+
+ Returns the domain name for which this object returns information.
+
+ \sa setLabel()
+*/
+
+/*!
+ \enum TQDns::RecordType
+
+ This enum type defines the record types TQDns 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 TQDns 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
+ \link http://www.dns.net/dnsrd/rfc/rfc2535.html RFC-2535 \endlink
+ 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 TQDns::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 TQDns::startQuery()
+{
+ // isWorking() starts the query (if necessary)
+ if ( !isWorking() )
+ emit resultsReady();
+}
+
+/*!
+ The three functions TQDns::TQDns(TQString, RecordType),
+ TQDns::setLabel() and TQDns::setRecordType() may start a DNS lookup.
+ This function handles setting up the single shot timer.
+*/
+void TQDns::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 TQTimer( 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.
+*/
+TQString TQDns::toInAddrArpaDomain( const TQHostAddress &address )
+{
+ TQString 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 = "ip6.arpa";
+ uint b = 0;
+ while( b < 16 ) {
+ s = TQString::number( i.c[b]%16, 16 ) + "." +
+ TQString::number( i.c[b]/16, 16 ) + "." + s;
+ b++;
+ }
+ }
+ return s;
+}
+
+
+/*!
+ \fn TQDns::RecordType TQDns::recordType() const
+
+ Returns the record type of this DNS query object.
+
+ \sa setRecordType() RecordType
+*/
+
+/*!
+ \fn void TQDns::resultsReady()
+
+ This signal is emitted when results are available for one of the
+ qualifiedNames().
+*/
+
+/*!
+ Returns TRUE if TQDns is doing a lookup for this object (i.e. if it
+ does not already have the necessary information); otherwise
+ returns FALSE.
+
+ TQDns emits the resultsReady() signal when the status changes to FALSE.
+*/
+
+bool TQDns::isWorking() const
+{
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::isWorking (%s, %d)", l.ascii(), t );
+#endif
+ if ( t == None )
+ return FALSE;
+
+#if defined(Q_DNS_SYNCHRONOUS)
+ if ( d->noEventLoop )
+ return TRUE;
+#endif
+
+ TQPtrList<TQDnsRR> * ll = TQDnsDomain::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 TQDns object
+ has a recordType() of \c TQDns::A or \c TQDns::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.
+ \code
+ TQValueList<TQHostAddress> list = myDns.addresses();
+ TQValueList<TQHostAddress>::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+*/
+
+TQValueList<TQHostAddress> TQDns::addresses() const
+{
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::addresses (%s)", l.ascii() );
+#endif
+ TQValueList<TQHostAddress> result;
+ if ( t != A && t != Aaaa )
+ return result;
+
+ TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this );
+
+ TQDnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain )
+ result.append( rr->address );
+ cached->next();
+ }
+ delete cached;
+ return result;
+}
+
+
+/*!
+ \class TQDns::MailServer
+ \brief The TQDns::MailServer class is described in TQDns::mailServers().
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+
+ \internal
+*/
+
+/*!
+ Returns a list of mail servers if the record type is \c Mx. The
+ class \c TQDns::MailServer contains the following public variables:
+ \list
+ \i TQString TQDns::MailServer::name
+ \i Q_UINT16 TQDns::MailServer::priority
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \code
+ TQValueList<TQDns::MailServer> list = myDns.mailServers();
+ TQValueList<TQDns::MailServer>::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+*/
+TQValueList<TQDns::MailServer> TQDns::mailServers() const
+{
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::mailServers (%s)", l.ascii() );
+#endif
+ TQValueList<TQDns::MailServer> result;
+ if ( t != Mx )
+ return result;
+
+ TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this );
+
+ TQDnsRR * 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 TQDns::Server
+ \brief The TQDns::Server class is described in TQDns::servers().
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+
+ \internal
+*/
+
+/*!
+ Returns a list of servers if the record type is \c Srv. The class
+ \c TQDns::Server contains the following public variables:
+ \list
+ \i TQString TQDns::Server::name
+ \i Q_UINT16 TQDns::Server::priority
+ \i Q_UINT16 TQDns::Server::weight
+ \i Q_UINT16 TQDns::Server::port
+ \endlist
+
+ Note that if you want to iterate over the list, you should iterate
+ over a copy, e.g.
+ \code
+ TQValueList<TQDns::Server> list = myDns.servers();
+ TQValueList<TQDns::Server>::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+*/
+TQValueList<TQDns::Server> TQDns::servers() const
+{
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::servers (%s)", l.ascii() );
+#endif
+ TQValueList<TQDns::Server> result;
+ if ( t != Srv )
+ return result;
+
+ TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this );
+
+ TQDnsRR * 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.
+ \code
+ TQStringList list = myDns.hostNames();
+ TQStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+
+*/
+TQStringList TQDns::hostNames() const
+{
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::hostNames (%s)", l.ascii() );
+#endif
+ TQStringList result;
+ if ( t != Ptr )
+ return result;
+
+ TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this );
+
+ TQDnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ TQString 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.
+ \code
+ TQStringList list = myDns.texts();
+ TQStringList::Iterator it = list.begin();
+ while( it != list.end() ) {
+ myProcessing( *it );
+ ++it;
+ }
+ \endcode
+*/
+TQStringList TQDns::texts() const
+{
+#if defined(TQDNS_DEBUG)
+ qDebug( "TQDns::texts (%s)", l.ascii() );
+#endif
+ TQStringList result;
+ if ( t != Txt )
+ return result;
+
+ TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( this );
+
+ TQDnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain ) {
+ TQString 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 TQDns is
+ "trolltech.com", then the canonical name for all of "lillian",
+ "l", "lillian.troll.no." and "l.trolltech.com" is
+ "lillian.troll.no.".
+*/
+
+TQString TQDns::canonicalName() const
+{
+ // the cname should work regardless of the recordType(), so set the record
+ // type temporarily to cname when you look at the cache
+ TQDns *that = (TQDns*) this; // mutable function
+ RecordType oldType = t;
+ that->t = Cname;
+ TQPtrList<TQDnsRR> * cached = TQDnsDomain::cached( that );
+ that->t = oldType;
+
+ TQDnsRR * rr;
+ while( (rr=cached->current()) != 0 ) {
+ if ( rr->current && !rr->nxdomain && rr->domain ) {
+ delete cached;
+ return rr->target;
+ }
+ cached->next();
+ }
+ delete cached;
+ return TQString::null;
+}
+
+#if defined(Q_DNS_SYNCHRONOUS)
+/*! \reimp
+*/
+void TQDns::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 TQDns::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 TQString getWindowsRegString( HKEY key, const TQString &subKey )
+{
+ TQString 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 = TQString::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 = 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 = 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 = ptr;
+ delete [] ptr;
+ }
+ } );
+ return s;
+}
+
+static bool getDnsParamsFromRegistry( const TQString &path,
+ TQString *domainName, TQString *nameServer, TQString *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,
+ 0, KEY_READ, &k );
+ } );
+
+ if ( r == ERROR_SUCCESS ) {
+ *domainName = getWindowsRegString( k, "DhcpDomain" );
+ if ( domainName->isEmpty() )
+ *domainName = getWindowsRegString( k, "Domain" );
+
+ *nameServer = getWindowsRegString( k, "DhcpNameServer" );
+ if ( nameServer->isEmpty() )
+ *nameServer = getWindowsRegString( k, "NameServer" );
+
+ *searchList = getWindowsRegString( k, "SearchList" );
+ }
+ RegCloseKey( k );
+ return r == ERROR_SUCCESS;
+}
+
+void TQDns::doResInit()
+{
+ char separator = 0;
+
+ if ( ns )
+ delete ns;
+ ns = new TQPtrList<TQHostAddress>;
+ ns->setAutoDelete( TRUE );
+ domains = new TQStrList( TRUE );
+ domains->setAutoDelete( TRUE );
+
+ TQString domainName, nameServer, searchList;
+
+ bool gotNetworkParams = FALSE;
+ // try the API call GetNetworkParams() first and use registry lookup only
+ // as a fallback
+#ifdef Q_OS_TEMP
+ HINSTANCE hinstLib = LoadLibraryW( L"iphlpapi" );
+#else
+ HINSTANCE hinstLib = LoadLibraryA( "iphlpapi" );
+#endif
+ if ( hinstLib != 0 ) {
+#ifdef Q_OS_TEMP
+ 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 = finfo->DomainName;
+ nameServer = "";
+ IP_ADDR_STRING *dnsServer = &finfo->DnsServerList;
+ while ( dnsServer != 0 ) {
+ nameServer += dnsServer->IpAddress.String;
+ dnsServer = dnsServer->Next;
+ if ( dnsServer != 0 )
+ nameServer += " ";
+ }
+ searchList = "";
+ separator = ' ';
+ gotNetworkParams = TRUE;
+ }
+ delete[] finfo;
+ }
+ }
+ FreeLibrary( hinstLib );
+ }
+ if ( !gotNetworkParams ) {
+ if ( getDnsParamsFromRegistry(
+ TQString( "System\\CurrentControlSet\\Services\\Tcpip\\Parameters" ),
+ &domainName, &nameServer, &searchList )) {
+ // for NT
+ separator = ' ';
+ } else if ( getDnsParamsFromRegistry(
+ TQString( "System\\CurrentControlSet\\Services\\VxD\\MSTCP" ),
+ &domainName, &nameServer, &searchList )) {
+ // for 95/98
+ separator = ',';
+ } else {
+ // Could not access the TCP/IP parameters
+ domainName = "";
+ nameServer = "127.0.0.1";
+ searchList = "";
+ separator = ' ';
+ }
+ }
+
+ nameServer = nameServer.simplifyWhiteSpace();
+ int first, last;
+ if ( !nameServer.isEmpty() ) {
+ first = 0;
+ do {
+ last = nameServer.find( separator, first );
+ if ( last < 0 )
+ last = nameServer.length();
+ TQDns tmp( nameServer.mid( first, last-first ), TQDns::A );
+ TQValueList<TQHostAddress> address = tmp.addresses();
+ Q_LONG i = address.count();
+ while( i )
+ ns->append( new TQHostAddress(address[--i]) );
+ first = last+1;
+ } while( first < (int)nameServer.length() );
+ }
+
+ searchList = searchList + " " + domainName;
+ searchList = searchList.simplifyWhiteSpace().lower();
+ first = 0;
+ do {
+ last = searchList.find( separator, first );
+ if ( last < 0 )
+ last = searchList.length();
+ domains->append( qstrdup( searchList.mid( first, last-first ) ) );
+ first = last+1;
+ } while( first < (int)searchList.length() );
+}
+
+#elif defined(Q_OS_UNIX)
+
+#if defined(Q_DNS_SYNCHRONOUS)
+void TQDns::doSynchronousLookup()
+{
+ if ( t!=None && !l.isEmpty() ) {
+ TQValueListIterator<TQString> it = n.begin();
+ TQValueListIterator<TQString> end = n.end();
+ int type;
+ switch( t ) {
+ case TQDns::A:
+ type = 1;
+ break;
+ case TQDns::Aaaa:
+ type = 28;
+ break;
+ case TQDns::Mx:
+ type = 15;
+ break;
+ case TQDns::Srv:
+ type = 33;
+ break;
+ case TQDns::Cname:
+ type = 5;
+ break;
+ case TQDns::Ptr:
+ type = 12;
+ break;
+ case TQDns::Txt:
+ type = 16;
+ break;
+ default:
+ type = (char)255; // any
+ break;
+ }
+ while( it != end ) {
+ TQString s = *it;
+ it++;
+ TQByteArray ba( 512 );
+ int len = res_search( s.latin1(), 1, type, (uchar*)ba.data(), ba.size() );
+ if ( len > 0 ) {
+ ba.resize( len );
+
+ TQDnsQuery * query = new TQDnsQuery;
+ query->started = now();
+ query->id = ++::id;
+ query->t = t;
+ query->l = s;
+ TQDnsAnswer 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
+#else
+#endif
+
+void TQDns::doResInit()
+{
+ if ( ns )
+ return;
+ ns = new TQPtrList<TQHostAddress>;
+ ns->setAutoDelete( TRUE );
+ domains = new TQStrList( TRUE );
+ domains->setAutoDelete( TRUE );
+
+ // read resolv.conf manually.
+ TQFile resolvConf("/etc/resolv.conf");
+ if (resolvConf.open(IO_ReadOnly)) {
+ TQTextStream stream( &resolvConf );
+ TQString line;
+
+ while ( !stream.atEnd() ) {
+ line = stream.readLine();
+ TQStringList list = TQStringList::split( " ", line );
+ if ( line.startsWith( "#" ) || list.count() < 2 )
+ continue;
+ const TQString type = list[0].lower();
+
+ if ( type == "nameserver" ) {
+ TQHostAddress *address = new TQHostAddress();
+ if ( address->setAddress( TQString(list[1]) ) ) {
+ // only add ipv6 addresses from resolv.conf if
+ // this host supports ipv6.
+ if ( address->isIPv4Address() || ipv6support )
+ ns->append( address );
+ else
+ delete address;
+ } else {
+ delete address;
+ }
+ } else if ( type == "search" ) {
+ TQStringList srch = TQStringList::split( " ", list[1] );
+ for ( TQStringList::Iterator i = srch.begin(); i != srch.end(); ++i )
+ domains->append( (*i).lower() );
+
+ } else if ( type == "domain" ) {
+ domains->append( list[1].lower() );
+ }
+ }
+ }
+
+ if (ns->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++ )
+ ns->append( new TQHostAddress( 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]) )
+ domains->append( TQString::fromLatin1( res.dnsrch[i] ).lower() );
+ else
+ break;
+ }
+# endif
+ if ( *res.defdname )
+ domains->append( TQString::fromLatin1( res.defdname ).lower() );
+#else
+ qdns_res_init();
+ int i;
+ // find the name servers to use
+ for( i=0; i < MAXNS && i < _res.nscount; i++ )
+ ns->append( new TQHostAddress( 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]) )
+ domains->append( TQString::fromLatin1( _res.dnsrch[i] ).lower() );
+ else
+ break;
+ }
+# endif
+ if ( *_res.defdname )
+ domains->append( TQString::fromLatin1( _res.defdname ).lower() );
+#endif
+
+ // the code above adds "0.0.0.0" as a name server at the slightest
+ // hint of trouble. so remove those again.
+ ns->first();
+ while( ns->current() ) {
+ if ( ns->current()->isNull() )
+ delete ns->take();
+ else
+ ns->next();
+ }
+ }
+
+ TQFile hosts( TQString::fromLatin1( "/etc/hosts" ) );
+ if ( hosts.open( IO_ReadOnly ) ) {
+ // read the /etc/hosts file, creating long-life A and PTR RRs
+ // for the things we find.
+ TQTextStream i( &hosts );
+ TQString line;
+ while( !i.atEnd() ) {
+ line = i.readLine().simplifyWhiteSpace().lower();
+ uint n = 0;
+ while( n < line.length() && line[(int)n] != '#' )
+ n++;
+ line.truncate( n );
+ n = 0;
+ while( n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ TQString ip = line.left( n );
+ TQHostAddress a;
+ a.setAddress( ip );
+ if ( ( a.isIPv4Address() || a.isIPv6Address() ) && !a.isNull() ) {
+ bool first = TRUE;
+ line = line.mid( n+1 );
+ n = 0;
+ while( n < line.length() && !line[(int)n].isSpace() )
+ n++;
+ TQString hostname = line.left( n );
+ // ### in case of bad syntax, hostname is invalid. do we care?
+ if ( n ) {
+ TQDnsRR * rr = new TQDnsRR( hostname );
+ if ( a.isIPv4Address() )
+ rr->t = TQDns::A;
+ else
+ rr->t = TQDns::Aaaa;
+ rr->address = a;
+ rr->deleteTime = UINT_MAX;
+ rr->expireTime = UINT_MAX;
+ rr->current = TRUE;
+ if ( first ) {
+ first = FALSE;
+ TQDnsRR * ptr = new TQDnsRR( TQDns::toInAddrArpaDomain( a ) );
+ ptr->t = TQDns::Ptr;
+ ptr->target = hostname;
+ ptr->deleteTime = UINT_MAX;
+ ptr->expireTime = UINT_MAX;
+ ptr->current = TRUE;
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+#endif // QT_NO_DNS
diff --git a/src/network/qdns.h b/src/network/qdns.h
new file mode 100644
index 00000000..b75833ef
--- /dev/null
+++ b/src/network/qdns.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Definition of TQDns class.
+**
+** Created : 991122
+**
+** Copyright (C) 1999-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQDNS_H
+#define TQDNS_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qhostaddress.h"
+#include "qsocketnotifier.h"
+#include "qstringlist.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_DNS
+#else
+#define TQM_EXPORT_DNS Q_EXPORT
+#endif
+
+#ifndef QT_NO_DNS
+
+//#define Q_DNS_SYNCHRONOUS
+
+class TQDnsPrivate;
+
+class TQM_EXPORT_DNS TQDns: public TQObject {
+ Q_OBJECT
+public:
+ enum RecordType {
+ None,
+ A, Aaaa,
+ Mx, Srv,
+ Cname,
+ Ptr,
+ Txt
+ };
+
+ TQDns();
+ TQDns( const TQString & label, RecordType rr = A );
+ TQDns( const TQHostAddress & address, RecordType rr = Ptr );
+ virtual ~TQDns();
+
+ // to set/change the query
+ virtual void setLabel( const TQString & label );
+ virtual void setLabel( const TQHostAddress & address );
+ TQString 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
+ TQValueList<TQHostAddress> addresses() const;
+
+ class TQM_EXPORT_DNS MailServer {
+ public:
+ MailServer( const TQString & n=TQString::null, Q_UINT16 p=0 )
+ :name(n), priority(p) {}
+ TQString name;
+ Q_UINT16 priority;
+ Q_DUMMY_COMPARISON_OPERATOR(MailServer)
+ };
+ TQValueList<MailServer> mailServers() const;
+
+ class TQM_EXPORT_DNS Server {
+ public:
+ Server(const TQString & n=TQString::null, Q_UINT16 p=0, Q_UINT16 w=0, Q_UINT16 po=0 )
+ : name(n), priority(p), weight(w), port(po) {}
+ TQString name;
+ Q_UINT16 priority;
+ Q_UINT16 weight;
+ Q_UINT16 port;
+ Q_DUMMY_COMPARISON_OPERATOR(Server)
+ };
+ TQValueList<Server> servers() const;
+
+ TQStringList hostNames() const;
+
+ TQStringList texts() const;
+
+ TQString canonicalName() const; // ### real-world but uncommon: TQStringList
+
+ TQStringList qualifiedNames() const { return n; }
+
+#if defined(Q_DNS_SYNCHRONOUS)
+protected:
+ void connectNotify( const char *signal );
+#endif
+
+signals:
+ void resultsReady();
+
+private slots:
+ void startQuery();
+
+private:
+ static void doResInit();
+ void setStartQueryTimer();
+ static TQString toInAddrArpaDomain( const TQHostAddress &address );
+#if defined(Q_DNS_SYNCHRONOUS)
+ void doSynchronousLookup();
+#endif
+
+ TQString l;
+ TQStringList n;
+ RecordType t;
+ TQDnsPrivate * d;
+
+ friend class TQDnsAnswer;
+ friend class TQDnsManager;
+};
+
+
+// TQDnsSocket are sockets that are used for DNS lookup
+
+class TQDnsSocket: public TQObject {
+ Q_OBJECT
+ // note: Private not public. This class contains NO public API.
+protected:
+ TQDnsSocket( TQObject *, const char * );
+ virtual ~TQDnsSocket();
+
+private slots:
+ virtual void cleanCache();
+ virtual void retransmit();
+ virtual void answer();
+};
+
+#endif // QT_NO_DNS
+
+#endif // TQDNS_H
diff --git a/src/network/qftp.cpp b/src/network/qftp.cpp
new file mode 100644
index 00000000..560917e3
--- /dev/null
+++ b/src/network/qftp.cpp
@@ -0,0 +1,2419 @@
+/****************************************************************************
+**
+** Implementation of TQFtp class.
+**
+** Created : 970521
+**
+** Copyright (C) 1997-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qftp.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+
+#include "qsocket.h"
+#include "qsocketdevice.h"
+#include "qurlinfo.h"
+#include "qurloperator.h"
+#include "qstringlist.h"
+#include "qregexp.h"
+#include "qtimer.h"
+#include "qfileinfo.h"
+#include "qptrdict.h" // binary compatibility
+
+#ifndef QT_NO_TEXTCODEC
+#include "qtextcodec.h"
+#endif
+
+//#define TQFTPPI_DEBUG
+//#define TQFTPDTP_DEBUG
+
+
+class TQFtpPI;
+
+class TQFtpDTP : public TQObject
+{
+ Q_OBJECT
+
+public:
+ enum ConnectState {
+ CsHostFound,
+ CsConnected,
+ CsClosed,
+ CsHostNotFound,
+ CsConnectionRefused
+ };
+
+ TQFtpDTP( TQFtpPI *p, TQObject *parent=0, const char *name=0 );
+
+ void setData( TQByteArray * );
+ void setDevice( TQIODevice * );
+ void writeData();
+
+ void setBytesTotal( int bytes )
+ {
+ bytesTotal = bytes;
+ bytesDone = 0;
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ }
+
+ bool hasError() const;
+ TQString errorMessage() const;
+ void clearError();
+
+ void connectToHost( const TQString & host, Q_UINT16 port )
+ { socket.connectToHost( host, port ); }
+
+ TQSocket::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;
+ }
+
+ TQByteArray readAll()
+ {
+ TQByteArray tmp = socket.readAll();
+ bytesDone += tmp.size();
+ return tmp;
+ }
+
+ void abortConnection();
+
+ static bool parseDir( const TQString &buffer, const TQString &userName, TQUrlInfo *info );
+
+signals:
+ void listInfo( const TQUrlInfo& );
+ 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;
+ }
+
+ TQSocket socket;
+ TQFtpPI *pi;
+ TQString 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 {
+ TQByteArray *ba;
+ TQIODevice *dev;
+ } data;
+ bool is_ba;
+};
+
+class TQFtpPI : public TQObject
+{
+ Q_OBJECT
+
+public:
+ TQFtpPI( TQObject *parent = 0 );
+
+ void connectToHost( const TQString &host, Q_UINT16 port );
+
+ bool sendCommands( const TQStringList &cmds );
+ bool sendCommand( const TQString &cmd )
+ { return sendCommands( TQStringList( cmd ) ); }
+
+ void clearPendingCommands();
+ void abort();
+
+ TQString currentCommand() const
+ { return currentCmd; }
+
+ bool rawCommand;
+
+ TQFtpDTP 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 TQString& );
+ void error( int, const TQString& );
+ void rawFtpReply( int, const TQString& );
+
+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();
+
+ TQSocket commandSocket;
+ TQString replyText;
+ char replyCode[3];
+ State state;
+ AbortState abortState;
+ TQStringList pendingCommands;
+ TQString currentCmd;
+
+ bool waitForDtpToConnect;
+ bool waitForDtpToClose;
+};
+
+/**********************************************************************
+ *
+ * TQFtpCommand implemenatation
+ *
+ *********************************************************************/
+class TQFtpCommand
+{
+public:
+ TQFtpCommand( TQFtp::Command cmd, TQStringList raw );
+ TQFtpCommand( TQFtp::Command cmd, TQStringList raw, const TQByteArray &ba );
+ TQFtpCommand( TQFtp::Command cmd, TQStringList raw, TQIODevice *dev );
+ ~TQFtpCommand();
+
+ int id;
+ TQFtp::Command command;
+ TQStringList rawCmds;
+
+ // If is_ba is TRUE, ba is used; ba is never 0.
+ // Otherwise dev is used; dev can be 0 or not.
+ union {
+ TQByteArray *ba;
+ TQIODevice *dev;
+ } data;
+ bool is_ba;
+
+ static int idCounter;
+};
+
+int TQFtpCommand::idCounter = 0;
+
+TQFtpCommand::TQFtpCommand( TQFtp::Command cmd, TQStringList raw )
+ : command(cmd), rawCmds(raw), is_ba(FALSE)
+{
+ id = ++idCounter;
+ data.dev = 0;
+}
+
+TQFtpCommand::TQFtpCommand( TQFtp::Command cmd, TQStringList raw, const TQByteArray &ba )
+ : command(cmd), rawCmds(raw), is_ba(TRUE)
+{
+ id = ++idCounter;
+ data.ba = new TQByteArray( ba );
+}
+
+TQFtpCommand::TQFtpCommand( TQFtp::Command cmd, TQStringList raw, TQIODevice *dev )
+ : command(cmd), rawCmds(raw), is_ba(FALSE)
+{
+ id = ++idCounter;
+ data.dev = dev;
+}
+
+TQFtpCommand::~TQFtpCommand()
+{
+ if ( is_ba )
+ delete data.ba;
+}
+
+/**********************************************************************
+ *
+ * TQFtpDTP implemenatation
+ *
+ *********************************************************************/
+TQFtpDTP::TQFtpDTP( TQFtpPI *p, TQObject *parent, const char *name ) :
+ TQObject( parent, name ),
+ socket( 0, "TQFtpDTP_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 TQFtpDTP::setData( TQByteArray *ba )
+{
+ is_ba = TRUE;
+ data.ba = ba;
+}
+
+void TQFtpDTP::setDevice( TQIODevice *dev )
+{
+ is_ba = FALSE;
+ data.dev = dev;
+}
+
+void TQFtpDTP::writeData()
+{
+ if ( is_ba ) {
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::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(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::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 TQFtpDTP::hasError() const
+{
+ return !err.isNull();
+}
+
+inline TQString TQFtpDTP::errorMessage() const
+{
+ return err;
+}
+
+inline void TQFtpDTP::clearError()
+{
+ err = TQString::null;
+}
+
+void TQFtpDTP::abortConnection()
+{
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::abortConnection" );
+#endif
+ callWriteData = FALSE;
+ clearData();
+
+ socket.clearPendingData();
+ socket.close();
+}
+
+bool TQFtpDTP::parseDir( const TQString &buffer, const TQString &userName, TQUrlInfo *info )
+{
+ TQStringList lst = TQStringList::split( " ", buffer );
+
+ if ( lst.count() < 9 )
+ return FALSE;
+
+ TQString tmp;
+
+ // permissions
+ tmp = lst[ 0 ];
+
+ if ( tmp[ 0 ] == TQChar( 'd' ) ) {
+ info->setDir( TRUE );
+ info->setFile( FALSE );
+ info->setSymLink( FALSE );
+ } else if ( tmp[ 0 ] == TQChar( '-' ) ) {
+ info->setDir( FALSE );
+ info->setFile( TRUE );
+ info->setSymLink( FALSE );
+ } else if ( tmp[ 0 ] == TQChar( '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 ] == 'r');
+ perms[0][1] = (tmp[ 2 ] == 'w');
+ perms[0][2] = (tmp[ 3 ] == 'x');
+ perms[1][0] = (tmp[ 4 ] == 'r');
+ perms[1][1] = (tmp[ 5 ] == 'w');
+ perms[1][2] = (tmp[ 6 ] == 'x');
+ perms[2][0] = (tmp[ 7 ] == 'r');
+ perms[2][1] = (tmp[ 8 ] == 'w');
+ perms[2][2] = (tmp[ 9 ] == '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 |= TQUrlInfo::ReadOwner;
+ if ( perms[ user ][ writable ] )
+ p |= TQUrlInfo::WriteOwner;
+ if ( perms[ user ][ executable ] )
+ p |= TQUrlInfo::ExeOwner;
+ if ( perms[ group ][ readable ] )
+ p |= TQUrlInfo::ReadGroup;
+ if ( perms[ group ][ writable ] )
+ p |= TQUrlInfo::WriteGroup;
+ if ( perms[ group ][ executable ] )
+ p |= TQUrlInfo::ExeGroup;
+ if ( perms[ other ][ readable ] )
+ p |= TQUrlInfo::ReadOther;
+ if ( perms[ other ][ writable ] )
+ p |= TQUrlInfo::WriteOther;
+ if ( perms[ other ][ executable ] )
+ p |= TQUrlInfo::ExeOther;
+ info->setPermissions( p );
+
+ // size
+ tmp = lst[ 4 ];
+ info->setSize( tmp.toInt() );
+
+ // date and time
+ TQTime time;
+ TQString dateStr;
+ dateStr += "Sun ";
+ lst[ 5 ][ 0 ] = lst[ 5 ][ 0 ].upper();
+ dateStr += lst[ 5 ];
+ dateStr += ' ';
+ dateStr += lst[ 6 ];
+ dateStr += ' ';
+
+ if ( lst[ 7 ].contains( ":" ) ) {
+ time = TQTime( lst[ 7 ].left( 2 ).toInt(), lst[ 7 ].right( 2 ).toInt() );
+ dateStr += TQString::number( TQDate::currentDate().year() );
+ } else {
+ dateStr += lst[ 7 ];
+ }
+
+ TQDate date = TQDate::fromString( dateStr );
+ info->setLastModified( TQDateTime( date, time ) );
+
+ if ( lst[ 7 ].contains( ":" ) ) {
+ const int futureTolerance = 600;
+ if( info->lastModified().secsTo( TQDateTime::currentDateTime() ) < -futureTolerance ) {
+ TQDateTime dt = info->lastModified();
+ TQDate 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 {
+ TQString n;
+ for ( uint i = 8; i < lst.count(); ++i )
+ n += lst[ i ] + " ";
+ n = n.stripWhiteSpace();
+ info->setName( n );
+ }
+ return TRUE;
+}
+
+void TQFtpDTP::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(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::connectState( CsConnected )" );
+#endif
+ emit connectState( TQFtpDTP::CsConnected );
+}
+
+void TQFtpDTP::socketReadyRead()
+{
+ if ( pi->currentCommand().isEmpty() ) {
+ socket.close();
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::connectState( CsClosed )" );
+#endif
+ emit connectState( TQFtpDTP::CsClosed );
+ return;
+ }
+
+ if ( pi->currentCommand().startsWith("LIST") ) {
+ while ( socket.canReadLine() ) {
+ TQUrlInfo i;
+ TQString line = socket.readLine();
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP read (list): '%s'", line.latin1() );
+#endif
+ if ( parseDir( line, "", &i ) ) {
+ emit listInfo( i );
+ } else {
+ // some FTP servers don't return a 550 if the file or directory
+ // does not exist, but rather write a text to the data socket
+ // -- try to catch these cases
+ if ( line.endsWith( "No such file or directory\r\n" ) )
+ err = line;
+ }
+ }
+ } else {
+ if ( !is_ba && data.dev ) {
+ TQByteArray 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(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP 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(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP readyRead: %d bytes available (total %d bytes read)", (int)bytesAvailable(), bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone+socket.bytesAvailable(), bytesTotal );
+ emit readyRead();
+ }
+ }
+}
+
+void TQFtpDTP::socketError( int e )
+{
+ if ( e == TQSocket::ErrHostNotFound ) {
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::connectState( CsHostNotFound )" );
+#endif
+ emit connectState( TQFtpDTP::CsHostNotFound );
+ } else if ( e == TQSocket::ErrConnectionRefused ) {
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::connectState( CsConnectionRefused )" );
+#endif
+ emit connectState( TQFtpDTP::CsConnectionRefused );
+ }
+}
+
+void TQFtpDTP::socketConnectionClosed()
+{
+ if ( !is_ba && data.dev ) {
+ clearData();
+ }
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::connectState( CsClosed )" );
+#endif
+ emit connectState( TQFtpDTP::CsClosed );
+}
+
+void TQFtpDTP::socketBytesWritten( int bytes )
+{
+ bytesDone += bytes;
+#if defined(TQFTPDTP_DEBUG)
+ qDebug( "TQFtpDTP::bytesWritten( %d )", bytesDone );
+#endif
+ emit dataTransferProgress( bytesDone, bytesTotal );
+ if ( callWriteData )
+ writeData();
+}
+
+/**********************************************************************
+ *
+ * TQFtpPI implemenatation
+ *
+ *********************************************************************/
+TQFtpPI::TQFtpPI( TQObject *parent ) :
+ TQObject( parent ),
+ rawCommand(FALSE),
+ dtp( this ),
+ commandSocket( 0, "TQFtpPI_socket" ),
+ state( Begin ), abortState( None ),
+ currentCmd( TQString::null ),
+ 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 TQFtpPI::connectToHost( const TQString &host, Q_UINT16 port )
+{
+ emit connectState( TQFtp::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 TQFtpPI::sendCommands( const TQStringList &cmds )
+{
+ if ( !pendingCommands.isEmpty() )
+ return FALSE;
+
+ if ( commandSocket.state()!=TQSocket::Connected || state!=Idle ) {
+ emit error( TQFtp::NotConnected, TQFtp::tr( "Not connected" ) );
+ return TRUE; // there are no pending commands
+ }
+
+ pendingCommands = cmds;
+ startNextCmd();
+ return TRUE;
+}
+
+void TQFtpPI::clearPendingCommands()
+{
+ pendingCommands.clear();
+ dtp.abortConnection();
+ currentCmd = TQString::null;
+ state = Idle;
+}
+
+void TQFtpPI::abort()
+{
+ pendingCommands.clear();
+
+ if ( abortState != None )
+ // ABOR already sent
+ return;
+
+ abortState = AbortStarted;
+#if defined(TQFTPPI_DEBUG)
+ qDebug( "TQFtpPI send: ABOR" );
+#endif
+ commandSocket.writeBlock( "ABOR\r\n", 6 );
+
+ if ( currentCmd.startsWith("STOR ") )
+ dtp.abortConnection();
+}
+
+void TQFtpPI::hostFound()
+{
+ emit connectState( TQFtp::Connecting );
+}
+
+void TQFtpPI::connected()
+{
+ state = Begin;
+#if defined(TQFTPPI_DEBUG)
+// qDebug( "TQFtpPI state: %d [connected()]", state );
+#endif
+ emit connectState( TQFtp::Connected );
+}
+
+void TQFtpPI::connectionClosed()
+{
+ commandSocket.close();
+ emit connectState( TQFtp::Unconnected );
+}
+
+void TQFtpPI::delayedCloseFinished()
+{
+ emit connectState( TQFtp::Unconnected );
+}
+
+void TQFtpPI::error( int e )
+{
+ if ( e == TQSocket::ErrHostNotFound ) {
+ emit connectState( TQFtp::Unconnected );
+ emit error( TQFtp::HostNotFound,
+ TQFtp::tr( "Host %1 not found" ).arg( commandSocket.peerName() ) );
+ } else if ( e == TQSocket::ErrConnectionRefused ) {
+ emit connectState( TQFtp::Unconnected );
+ emit error( TQFtp::ConnectionRefused,
+ TQFtp::tr( "Connection refused to host %1" ).arg( commandSocket.peerName() ) );
+ }
+}
+
+void TQFtpPI::readyRead()
+{
+ if ( waitForDtpToClose )
+ return;
+
+ while ( commandSocket.canReadLine() ) {
+ // read line with respect to line continuation
+ TQString line = 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;
+ }
+ }
+ }
+ TQString endOfMultiLine;
+ endOfMultiLine[0] = '0' + replyCode[0];
+ endOfMultiLine[1] = '0' + replyCode[1];
+ endOfMultiLine[2] = '0' + replyCode[2];
+ endOfMultiLine[3] = ' ';
+ TQString lineCont( endOfMultiLine );
+ lineCont[3] = '-';
+ TQString lineLeft4 = line.left(4);
+
+ while ( lineLeft4 != endOfMultiLine ) {
+ if ( lineLeft4 == lineCont )
+ replyText += line.mid( 4 ); // strip 'xyz-'
+ else
+ replyText += line;
+ if ( !commandSocket.canReadLine() )
+ return;
+ line = commandSocket.readLine();
+ lineLeft4 = line.left(4);
+ }
+ replyText += line.mid( 4 ); // strip reply code 'xyz '
+ if ( replyText.endsWith("\r\n") )
+ replyText.truncate( replyText.length()-2 );
+
+ if ( processReply() )
+ replyText = "";
+ }
+}
+
+/*
+ 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 TQFtpPI::processReply()
+{
+#if defined(TQFTPPI_DEBUG)
+// qDebug( "TQFtpPI state: %d [processReply() begin]", state );
+ if ( replyText.length() < 400 )
+ qDebug( "TQFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.latin1() );
+ else
+ qDebug( "TQFtpPI 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() != TQSocket::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( TQFtp::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
+ state = table[ replyCode[0] - 1 ];
+ break;
+ default:
+ // ### spontaneous message
+ return TRUE;
+ }
+#if defined(TQFTPPI_DEBUG)
+// qDebug( "TQFtpPI 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.
+ TQRegExp addrPortPattern("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)");
+ if (addrPortPattern.search(replyText) == -1) {
+#if defined(TQFTPPI_DEBUG)
+ qDebug( "TQFtp: bad 227 response -- address and port information missing" );
+#endif
+ // ### error handling
+ } else {
+ TQStringList lst = addrPortPattern.capturedTexts();
+ TQString host = lst[1] + "." + lst[2] + "." + lst[3] + "." + 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("USER ") && pendingCommands.count()>0 &&
+ pendingCommands.first().startsWith("PASS ") ) {
+ // no need to send the PASS -- we are already logged in
+ pendingCommands.pop_front();
+ }
+ // 230 User logged in, proceed.
+ emit connectState( TQFtp::LoggedIn );
+ } else if ( replyCodeInt == 213 ) {
+ // 213 File status.
+ if ( currentCmd.startsWith("SIZE ") )
+ dtp.setBytesTotal( replyText.simplifyWhiteSpace().toInt() );
+ } else if ( replyCode[0]==1 && currentCmd.startsWith("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( TQFtp::UnknownError, dtp.errorMessage() );
+ dtp.clearError();
+ }
+ startNextCmd();
+ break;
+ case Waiting:
+ // ### do nothing
+ break;
+ case Failure:
+ emit error( TQFtp::UnknownError, replyText );
+ state = Idle;
+ startNextCmd();
+ break;
+ }
+#if defined(TQFTPPI_DEBUG)
+// qDebug( "TQFtpPI state: %d [processReply() end]", state );
+#endif
+ return TRUE;
+}
+
+#ifndef QT_NO_TEXTCODEC
+TQM_EXPORT_FTP TQTextCodec *qt_ftp_filename_codec = 0;
+#endif
+
+/*
+ Starts next pending command. Returns FALSE if there are no pending commands,
+ otherwise it returns TRUE.
+*/
+bool TQFtpPI::startNextCmd()
+{
+ if ( waitForDtpToConnect )
+ // don't process any new commands until we are connected
+ return TRUE;
+
+#if defined(TQFTPPI_DEBUG)
+ if ( state != Idle )
+ qDebug( "TQFtpPI startNextCmd: Internal error! TQFtpPI called in non-Idle state %d", state );
+#endif
+ if ( pendingCommands.isEmpty() ) {
+ currentCmd = TQString::null;
+ emit finished( replyText );
+ return FALSE;
+ }
+ currentCmd = pendingCommands.first();
+ pendingCommands.pop_front();
+#if defined(TQFTPPI_DEBUG)
+ qDebug( "TQFtpPI send: %s", currentCmd.left( currentCmd.length()-2 ).latin1() );
+#endif
+ state = Waiting;
+#ifndef QT_NO_TEXTCODEC
+ if ( qt_ftp_filename_codec ) {
+ int len = 0;
+ TQCString enc = qt_ftp_filename_codec->fromUnicode(currentCmd,len);
+ commandSocket.writeBlock( enc.data(), len );
+ } else
+#endif
+ {
+ commandSocket.writeBlock( currentCmd.latin1(), currentCmd.length() );
+ }
+ return TRUE;
+}
+
+void TQFtpPI::dtpConnectState( int s )
+{
+ switch ( s ) {
+ case TQFtpDTP::CsClosed:
+ if ( waitForDtpToClose ) {
+ // there is an unprocessed reply
+ if ( processReply() )
+ replyText = "";
+ else
+ return;
+ }
+ waitForDtpToClose = FALSE;
+ readyRead();
+ return;
+ case TQFtpDTP::CsConnected:
+ waitForDtpToConnect = FALSE;
+ startNextCmd();
+ return;
+ case TQFtpDTP::CsHostNotFound:
+ case TQFtpDTP::CsConnectionRefused:
+ emit error( TQFtp::ConnectionRefused,
+ TQFtp::tr( "Connection refused for data connection" ) );
+ startNextCmd();
+ return;
+ default:
+ return;
+ }
+}
+
+/**********************************************************************
+ *
+ * TQFtpPrivate
+ *
+ *********************************************************************/
+class TQFtpPrivate
+{
+public:
+ TQFtpPrivate() :
+ close_waitForStateChange(FALSE),
+ state( TQFtp::Unconnected ),
+ error( TQFtp::NoError ),
+ npWaitForLoginDone( FALSE )
+ { pending.setAutoDelete( TRUE ); }
+
+ TQFtpPI pi;
+ TQPtrList<TQFtpCommand> pending;
+ bool close_waitForStateChange;
+ TQFtp::State state;
+ TQFtp::Error error;
+ TQString errorString;
+
+ bool npWaitForLoginDone;
+};
+
+static TQPtrDict<TQFtpPrivate> *d_ptr = 0;
+static void cleanup_d_ptr()
+{
+ delete d_ptr;
+ d_ptr = 0;
+}
+static TQFtpPrivate* d( const TQFtp* foo )
+{
+ if ( !d_ptr ) {
+ d_ptr = new TQPtrDict<TQFtpPrivate>;
+ d_ptr->setAutoDelete( TRUE );
+ qAddPostRoutine( cleanup_d_ptr );
+ }
+ TQFtpPrivate* ret = d_ptr->find( (void*)foo );
+ if ( ! ret ) {
+ ret = new TQFtpPrivate;
+ d_ptr->replace( (void*) foo, ret );
+ }
+ return ret;
+}
+
+static void delete_d( const TQFtp* foo )
+{
+ if ( d_ptr )
+ d_ptr->remove( (void*) foo );
+}
+
+/**********************************************************************
+ *
+ * TQFtp implementation
+ *
+ *********************************************************************/
+/*!
+ \class TQFtp qftp.h
+ \brief The TQFtp class provides an implementation of the FTP protocol.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ This class provides two different interfaces: one is the
+ TQNetworkProtocol interface that allows you to use FTP through the
+ TQUrlOperator 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 TQFtp with the TQNetworkProtocol interface, you
+ do not use it directly, but rather through a TQUrlOperator, for
+ example:
+
+ \code
+ TQUrlOperator op( "ftp://ftp.trolltech.com" );
+ op.listChildren(); // Asks the server to provide a directory listing
+ \endcode
+
+ This code will only work if the TQFtp class is registered; to
+ register the class, you must call qInitNetworkProtocols() before
+ using a TQUrlOperator with TQFtp.
+
+ 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:
+
+ \code
+ TQFtp *ftp = new TQFtp( this ); // this is an optional TQObject parent
+ ftp->connectToHost( "ftp.trolltech.com" );
+ ftp->login();
+ \endcode
+
+ 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:
+
+ \code
+ ftp->connectToHost( "ftp.trolltech.com" ); // id == 1
+ ftp->login(); // id == 2
+ ftp->cd( "qt" ); // id == 3
+ ftp->get( "INSTALL" ); // id == 4
+ ftp->close(); // id == 5
+ \endcode
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \code
+ start( 1 )
+ stateChanged( HostLookup )
+ stateChanged( Connecting )
+ stateChanged( Connected )
+ finished( 1, FALSE )
+
+ start( 2 )
+ stateChanged( LoggedIn )
+ finished( 2, FALSE )
+
+ start( 3 )
+ finished( 3, FALSE )
+
+ start( 4 )
+ dataTransferProgress( 0, 3798 )
+ dataTransferProgress( 2896, 3798 )
+ readyRead()
+ dataTransferProgress( 3798, 3798 )
+ readyRead()
+ finished( 4, FALSE )
+
+ start( 5 )
+ stateChanged( Closing )
+ stateChanged( Unconnected )
+ finished( 5, FALSE )
+
+ done( FALSE )
+ \endcode
+
+ The dataTransferProgress() signal in the above example is useful
+ if you want to show a \link TQProgressBar progressbar \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:
+
+ \code
+ start( 1 )
+ stateChanged( HostLookup )
+ stateChanged( Connecting )
+ stateChanged( Connected )
+ finished( 1, FALSE )
+
+ start( 2 )
+ finished( 2, TRUE )
+
+ done( TRUE )
+ \endcode
+
+ 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
+ TQUrlOperator() 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 \link network.html TQt Network Documentation \endlink TQNetworkProtocol, TQUrlOperator TQHttp
+*/
+
+/*!
+ Constructs a TQFtp object.
+*/
+TQFtp::TQFtp() : TQNetworkProtocol()
+{
+ init();
+}
+
+/*!
+ Constructs a TQFtp object. The \a parent and \a name parameters
+ are passed to the TQObject constructor.
+*/
+TQFtp::TQFtp( TQObject *parent, const char *name ) : TQNetworkProtocol()
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+}
+
+void TQFtp::init()
+{
+ TQFtpPrivate *d = ::d( this );
+ d->errorString = tr( "Unknown error" );
+
+ connect( &d->pi, SIGNAL(connectState(int)),
+ SLOT(piConnectState(int)) );
+ connect( &d->pi, SIGNAL(finished(const TQString&)),
+ SLOT(piFinished(const TQString&)) );
+ connect( &d->pi, SIGNAL(error(int,const TQString&)),
+ SLOT(piError(int,const TQString&)) );
+ connect( &d->pi, SIGNAL(rawFtpReply(int,const TQString&)),
+ SLOT(piFtpReply(int,const TQString&)) );
+
+ 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(const TQUrlInfo&)),
+ SIGNAL(listInfo(const TQUrlInfo&)) );
+}
+
+/*!
+ \enum TQFtp::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 TQFtp::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 TQFtp::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 TQFtp::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 TQFtp::listInfo( const TQUrlInfo &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 TQFtp::commandStarted( int id )
+
+ This signal is emitted when processing the command identified by
+ \a id starts.
+
+ \sa commandFinished() done()
+*/
+
+/*!
+ \fn void TQFtp::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 TQFtp::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 TQFtp::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 TQFtp::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 TQFtp 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 TQProgressBar, 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() TQProgressBar::setProgress()
+*/
+
+/*!
+ \fn void TQFtp::rawCommandReply( int replyCode, const TQString &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 TQFtp::connectToHost( const TQString &host, Q_UINT16 port )
+{
+ TQStringList cmds;
+ cmds << host;
+ cmds << TQString::number( (uint)port );
+ return addCommand( new TQFtpCommand( 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 TQFtp::login( const TQString &user, const TQString &password )
+{
+ TQStringList cmds;
+ cmds << ( TQString("USER ") + ( user.isNull() ? TQString("anonymous") : user ) + "\r\n" );
+ cmds << ( TQString("PASS ") + ( password.isNull() ? TQString("anonymous@") : password ) + "\r\n" );
+ return addCommand( new TQFtpCommand( 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 TQFtp::close()
+{
+ return addCommand( new TQFtpCommand( Close, TQStringList("TQUIT\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 TQFtp::list( const TQString &dir )
+{
+ TQStringList cmds;
+ cmds << "TYPE A\r\n";
+ cmds << "PASV\r\n";
+ if ( dir.isEmpty() )
+ cmds << "LIST\r\n";
+ else
+ cmds << ( "LIST " + dir + "\r\n" );
+ return addCommand( new TQFtpCommand( 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 TQFtp::cd( const TQString &dir )
+{
+ return addCommand( new TQFtpCommand( Cd, TQStringList("CWD "+dir+"\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 TQFtp::get( const TQString &file, TQIODevice *dev )
+{
+ TQStringList cmds;
+ cmds << ( "SIZE " + file + "\r\n" );
+ cmds << "TYPE I\r\n";
+ cmds << "PASV\r\n";
+ cmds << ( "RETR " + file + "\r\n" );
+ if ( dev )
+ return addCommand( new TQFtpCommand( Get, cmds, dev ) );
+ return addCommand( new TQFtpCommand( 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 TQFtp::put( const TQByteArray &data, const TQString &file )
+{
+ TQStringList cmds;
+ cmds << "TYPE I\r\n";
+ cmds << "PASV\r\n";
+ cmds << ( "ALLO " + TQString::number(data.size()) + "\r\n" );
+ cmds << ( "STOR " + file + "\r\n" );
+ return addCommand( new TQFtpCommand( 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 TQFtp::put( TQIODevice *dev, const TQString &file )
+{
+ TQStringList cmds;
+ cmds << "TYPE I\r\n";
+ cmds << "PASV\r\n";
+ if ( !dev->isSequentialAccess() )
+ cmds << ( "ALLO " + TQString::number(dev->size()) + "\r\n" );
+ cmds << ( "STOR " + file + "\r\n" );
+ return addCommand( new TQFtpCommand( 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 TQFtp::remove( const TQString &file )
+{
+ return addCommand( new TQFtpCommand( Remove, TQStringList("DELE "+file+"\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 TQFtp::mkdir( const TQString &dir )
+{
+ return addCommand( new TQFtpCommand( Mkdir, TQStringList("MKD "+dir+"\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 TQFtp::rmdir( const TQString &dir )
+{
+ return addCommand( new TQFtpCommand( Rmdir, TQStringList("RMD "+dir+"\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 TQFtp::rename( const TQString &oldname, const TQString &newname )
+{
+ TQStringList cmds;
+ cmds << ( "RNFR " + oldname + "\r\n" );
+ cmds << ( "RNTO " + newname + "\r\n" );
+ return addCommand( new TQFtpCommand( 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 TQFtp 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 TQFtp::rawCommand( const TQString &command )
+{
+ TQString cmd = command.stripWhiteSpace() + "\r\n";
+ return addCommand( new TQFtpCommand( RawCommand, TQStringList(cmd) ) );
+}
+
+/*!
+ Returns the number of bytes that can be read from the data socket
+ at the moment.
+
+ \sa get() readyRead() readBlock() readAll()
+*/
+Q_ULONG TQFtp::bytesAvailable() const
+{
+ TQFtpPrivate *d = ::d( 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 TQFtp::readBlock( char *data, Q_ULONG maxlen )
+{
+ TQFtpPrivate *d = ::d( 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()
+*/
+TQByteArray TQFtp::readAll()
+{
+ TQFtpPrivate *d = ::d( 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 TQFtp::abort()
+{
+ TQFtpPrivate *d = ::d( 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 TQFtp::currentId() const
+{
+ TQFtpPrivate *d = ::d( this );
+ TQFtpCommand *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()
+*/
+TQFtp::Command TQFtp::currentCommand() const
+{
+ TQFtpPrivate *d = ::d( this );
+ TQFtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return None;
+ return c->command;
+}
+
+/*!
+ Returns the TQIODevice 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 TQIODevice in the slot connected to
+ the commandFinished() signal.
+
+ \sa get() put()
+*/
+TQIODevice* TQFtp::currentDevice() const
+{
+ TQFtpPrivate *d = ::d( this );
+ TQFtpCommand *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 TQFtp::hasPendingCommands() const
+{
+ TQFtpPrivate *d = ::d( 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 this as well, use abort().
+
+ \sa hasPendingCommands() abort()
+*/
+void TQFtp::clearPendingCommands()
+{
+ TQFtpPrivate *d = ::d( this );
+ TQFtpCommand *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()
+*/
+TQFtp::State TQFtp::state() const
+{
+ TQFtpPrivate *d = ::d( 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.
+*/
+TQFtp::Error TQFtp::error() const
+{
+ TQFtpPrivate *d = ::d( 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 TQt, the string has already passed through
+ tr().
+*/
+TQString TQFtp::errorString() const
+{
+ TQFtpPrivate *d = ::d( this );
+ return d->errorString;
+}
+
+int TQFtp::addCommand( TQFtpCommand *cmd )
+{
+ TQFtpPrivate *d = ::d( this );
+ d->pending.append( cmd );
+
+ if ( d->pending.count() == 1 )
+ // don't emit the commandStarted() signal before the id is returned
+ TQTimer::singleShot( 0, this, SLOT(startNextCommand()) );
+
+ return cmd->id;
+}
+
+void TQFtp::startNextCommand()
+{
+ TQFtpPrivate *d = ::d( this );
+
+ TQFtpCommand *c = d->pending.getFirst();
+ if ( c == 0 )
+ return;
+
+ d->error = NoError;
+ d->errorString = 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(IO_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 = TQFtp::Closing;
+ emit stateChanged( d->state );
+ }
+ if ( !d->pi.sendCommands( c->rawCmds ) ) {
+ // ### error handling (this case should not happen)
+ }
+ }
+}
+
+void TQFtp::piFinished( const TQString& )
+{
+ TQFtpPrivate *d = ::d( this );
+ TQFtpCommand *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 != TQFtp::Unconnected ) {
+ d->close_waitForStateChange = TRUE;
+ return;
+ }
+ }
+ emit commandFinished( c->id, FALSE );
+
+ d->pending.removeFirst();
+ if ( d->pending.isEmpty() ) {
+ emit done( FALSE );
+ } else {
+ startNextCommand();
+ }
+}
+
+void TQFtp::piError( int errorCode, const TQString &text )
+{
+ TQFtpPrivate *d = ::d( this );
+ TQFtpCommand *c = d->pending.getFirst();
+
+ // non-fatal errors
+ if ( c->command==Get && d->pi.currentCommand().startsWith("SIZE ") ) {
+ d->pi.dtp.setBytesTotal( -1 );
+ return;
+ } else if ( c->command==Put && d->pi.currentCommand().startsWith("ALLO ") ) {
+ return;
+ }
+
+ d->error = (Error)errorCode;
+ switch ( currentCommand() ) {
+ case ConnectToHost:
+ d->errorString = tr( "Connecting to host failed:\n%1" ).arg( text );
+ break;
+ case Login:
+ d->errorString = tr( "Login failed:\n%1" ).arg( text );
+ break;
+ case List:
+ d->errorString = tr( "Listing directory failed:\n%1" ).arg( text );
+ break;
+ case Cd:
+ d->errorString = tr( "Changing directory failed:\n%1" ).arg( text );
+ break;
+ case Get:
+ d->errorString = tr( "Downloading file failed:\n%1" ).arg( text );
+ break;
+ case Put:
+ d->errorString = tr( "Uploading file failed:\n%1" ).arg( text );
+ break;
+ case Remove:
+ d->errorString = tr( "Removing file failed:\n%1" ).arg( text );
+ break;
+ case Mkdir:
+ d->errorString = tr( "Creating directory failed:\n%1" ).arg( text );
+ break;
+ case Rmdir:
+ d->errorString = 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 TQFtp::piConnectState( int state )
+{
+ TQFtpPrivate *d = ::d( this );
+ d->state = (State)state;
+ emit stateChanged( d->state );
+ if ( d->close_waitForStateChange ) {
+ d->close_waitForStateChange = FALSE;
+ piFinished( tr( "Connection closed" ) );
+ }
+}
+
+void TQFtp::piFtpReply( int code, const TQString &text )
+{
+ if ( currentCommand() == RawCommand ) {
+ TQFtpPrivate *d = ::d( this );
+ d->pi.rawCommand = TRUE;
+ emit rawCommandReply( code, text );
+ }
+}
+
+/*!
+ Destructor.
+*/
+TQFtp::~TQFtp()
+{
+ abort();
+ close();
+ delete_d( this );
+}
+
+/**********************************************************************
+ *
+ * TQFtp implementation of the TQNetworkProtocol interface
+ *
+ *********************************************************************/
+/*! \reimp
+*/
+void TQFtp::operationListChildren( TQNetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? TQString( "/" ) : url()->path() ) );
+ list();
+ emit start( op );
+}
+
+/*! \reimp
+*/
+void TQFtp::operationMkDir( TQNetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ mkdir( op->arg( 0 ) );
+}
+
+/*! \reimp
+*/
+void TQFtp::operationRemove( TQNetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? TQString( "/" ) : url()->path() ) );
+ remove( TQUrl( op->arg( 0 ) ).path() );
+}
+
+/*! \reimp
+*/
+void TQFtp::operationRename( TQNetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ cd( ( url()->path().isEmpty() ? TQString( "/" ) : url()->path() ) );
+ rename( op->arg( 0 ), op->arg( 1 ));
+}
+
+/*! \reimp
+*/
+void TQFtp::operationGet( TQNetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ TQUrl u( op->arg( 0 ) );
+ get( u.path() );
+}
+
+/*! \reimp
+*/
+void TQFtp::operationPut( TQNetworkOperation *op )
+{
+ op->setState( StInProgress );
+
+ TQUrl u( op->arg( 0 ) );
+ put( op->rawArg(1), u.path() );
+}
+
+/*! \reimp
+*/
+bool TQFtp::checkConnection( TQNetworkOperation *op )
+{
+ TQFtpPrivate *d = ::d( this );
+ if ( state() == Unconnected && !d->npWaitForLoginDone ) {
+ connect( this, SIGNAL(listInfo(const TQUrlInfo&)),
+ this, SLOT(npListInfo(const TQUrlInfo&)) );
+ 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:
+ {
+ TQUrl 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;
+ }
+ TQString user = url()->user().isEmpty() ? TQString( "anonymous" ) : url()->user();
+ TQString pass = url()->password().isEmpty() ? TQString( "anonymous@" ) : url()->password();
+ login( user, pass );
+ }
+
+ if ( state() == LoggedIn )
+ return TRUE;
+ return FALSE;
+}
+
+/*! \reimp
+*/
+int TQFtp::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 TQFtp::parseDir( const TQString &buffer, TQUrlInfo &info )
+{
+ TQFtpDTP::parseDir( buffer, url()->user(), &info );
+}
+
+void TQFtp::npListInfo( const TQUrlInfo & i )
+{
+ if ( url() ) {
+ TQRegExp filt( url()->nameFilter(), FALSE, TRUE );
+ if ( i.isDir() || filt.search( i.name() ) != -1 ) {
+ emit newChild( i, operationInProgress() );
+ }
+ } else {
+ emit newChild( i, operationInProgress() );
+ }
+}
+
+void TQFtp::npDone( bool err )
+{
+ TQFtpPrivate *d = ::d( this );
+
+ bool emitFinishedSignal = FALSE;
+ TQNetworkOperation *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:
+ {
+ TQUrlInfo inf( op->arg( 0 ), 0, "", "", 0, TQDateTime(),
+ TQDateTime(), 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(const TQUrlInfo&)),
+ this, SLOT(npListInfo(const TQUrlInfo&)) );
+ 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 TQFtp::npStateChanged( int state )
+{
+ if ( url() ) {
+ if ( state == Connecting )
+ emit connectionStateChanged( ConHostFound, tr( "Host %1 found" ).arg( url()->host() ) );
+ else if ( state == Connected )
+ emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) );
+ else if ( state == Unconnected )
+ emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) );
+ } else {
+ if ( state == Connecting )
+ emit connectionStateChanged( ConHostFound, tr( "Host found" ) );
+ else if ( state == Connected )
+ emit connectionStateChanged( ConConnected, tr( "Connected to host" ) );
+ else if ( state == Unconnected )
+ emit connectionStateChanged( ConClosed, tr( "Connection closed" ) );
+ }
+}
+
+void TQFtp::npDataTransferProgress( int bDone, int bTotal )
+{
+ emit TQNetworkProtocol::dataTransferProgress( bDone, bTotal, operationInProgress() );
+}
+
+void TQFtp::npReadyRead()
+{
+ emit data( readAll(), operationInProgress() );
+}
+
+// ### unused -- delete in TQt 4.0
+/*! \internal
+*/
+void TQFtp::hostFound()
+{
+}
+/*! \internal
+*/
+void TQFtp::connected()
+{
+}
+/*! \internal
+*/
+void TQFtp::closed()
+{
+}
+/*! \internal
+*/
+void TQFtp::dataHostFound()
+{
+}
+/*! \internal
+*/
+void TQFtp::dataConnected()
+{
+}
+/*! \internal
+*/
+void TQFtp::dataClosed()
+{
+}
+/*! \internal
+*/
+void TQFtp::dataReadyRead()
+{
+}
+/*! \internal
+*/
+void TQFtp::dataBytesWritten( int )
+{
+}
+/*! \internal
+*/
+void TQFtp::error( int )
+{
+}
+
+#include "qftp.moc"
+
+#endif // QT_NO_NETWORKPROTOCOL_FTP
diff --git a/src/network/qftp.h b/src/network/qftp.h
new file mode 100644
index 00000000..ee615db7
--- /dev/null
+++ b/src/network/qftp.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Definition of TQFtp class.
+**
+** Created : 970521
+**
+** Copyright (C) 1997-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQFTP_H
+#define TQFTP_H
+
+#ifndef QT_H
+#include "qstring.h" // char*->TQString conversion
+#include "qurlinfo.h"
+#include "qnetworkprotocol.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_FTP
+#else
+#define TQM_EXPORT_FTP Q_EXPORT
+#endif
+
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+
+
+class TQSocket;
+class TQFtpCommand;
+
+class TQM_EXPORT_FTP TQFtp : public TQNetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ TQFtp(); // ### TQt 4.0: get rid of this overload
+ TQFtp( TQObject *parent, const char *name=0 );
+ virtual ~TQFtp();
+
+ int supportedOperations() const;
+
+ // non-TQNetworkProtocol 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 TQString &host, Q_UINT16 port=21 );
+ int login( const TQString &user=TQString::null, const TQString &password=TQString::null );
+ int close();
+ int list( const TQString &dir=TQString::null );
+ int cd( const TQString &dir );
+ int get( const TQString &file, TQIODevice *dev=0 );
+ int put( const TQByteArray &data, const TQString &file );
+ int put( TQIODevice *dev, const TQString &file );
+ int remove( const TQString &file );
+ int mkdir( const TQString &dir );
+ int rmdir( const TQString &dir );
+ int rename( const TQString &oldname, const TQString &newname );
+
+ int rawCommand( const TQString &command );
+
+ Q_ULONG bytesAvailable() const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ TQByteArray readAll();
+
+ int currentId() const;
+ TQIODevice* currentDevice() const;
+ Command currentCommand() const;
+ bool hasPendingCommands() const;
+ void clearPendingCommands();
+
+ State state() const;
+
+ Error error() const;
+ TQString errorString() const;
+
+public slots:
+ void abort();
+
+signals:
+ void stateChanged( int );
+ void listInfo( const TQUrlInfo& );
+ void readyRead();
+ void dataTransferProgress( int, int );
+ void rawCommandReply( int, const TQString& );
+
+ void commandStarted( int );
+ void commandFinished( int, bool );
+ void done( bool );
+
+protected:
+ void parseDir( const TQString &buffer, TQUrlInfo &info ); // ### TQt 4.0: delete this? (not public API)
+ void operationListChildren( TQNetworkOperation *op );
+ void operationMkDir( TQNetworkOperation *op );
+ void operationRemove( TQNetworkOperation *op );
+ void operationRename( TQNetworkOperation *op );
+ void operationGet( TQNetworkOperation *op );
+ void operationPut( TQNetworkOperation *op );
+
+ // ### TQt 4.0: delete these
+ // unused variables:
+ TQSocket *commandSocket, *dataSocket;
+ bool connectionReady, passiveMode;
+ int getTotalSize, getDoneSize;
+ bool startGetOnFail;
+ int putToWrite, putWritten;
+ bool errorInListChildren;
+
+private:
+ void init();
+ int addCommand( TQFtpCommand * );
+
+ bool checkConnection( TQNetworkOperation *op );
+
+private slots:
+ void startNextCommand();
+ void piFinished( const TQString& );
+ void piError( int, const TQString& );
+ void piConnectState( int );
+ void piFtpReply( int, const TQString& );
+
+private slots:
+ void npListInfo( const TQUrlInfo & );
+ void npDone( bool );
+ void npStateChanged( int );
+ void npDataTransferProgress( int, int );
+ void npReadyRead();
+
+protected slots:
+ // ### TQt 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
+
+#endif // TQFTP_H
diff --git a/src/network/qhostaddress.cpp b/src/network/qhostaddress.cpp
new file mode 100644
index 00000000..2c4e2e6f
--- /dev/null
+++ b/src/network/qhostaddress.cpp
@@ -0,0 +1,453 @@
+/****************************************************************************
+**
+** Implementation of TQHostAddress class.
+**
+** Created : 979899
+**
+** Copyright (C) 1997-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qhostaddress.h"
+#include "qstringlist.h"
+
+#ifndef QT_NO_NETWORK
+class TQHostAddressPrivate
+{
+public:
+ TQHostAddressPrivate( Q_UINT32 a_=0 ) : a(a_), isIp4(TRUE)
+ {
+ }
+ TQHostAddressPrivate( Q_UINT8 *a_ );
+ TQHostAddressPrivate(const Q_IPV6ADDR &a_);
+ ~TQHostAddressPrivate()
+ {
+ }
+
+ TQHostAddressPrivate & operator=( const TQHostAddressPrivate &from )
+ {
+ a = from.a;
+ isIp4 = from.isIp4;
+ a6 = from.a6;
+ return *this;
+ }
+
+private:
+ Q_UINT32 a; // ip 4 address
+ Q_IPV6ADDR a6; // ip 6 address
+ bool isIp4;
+
+ friend class TQHostAddress;
+};
+
+TQHostAddressPrivate::TQHostAddressPrivate(Q_UINT8 *a_) : a(0), isIp4(FALSE)
+{
+ for ( int i=0; i<16; i++ ) {
+ a6.c[i] = a_[i];
+ }
+}
+
+TQHostAddressPrivate::TQHostAddressPrivate(const Q_IPV6ADDR &a_) : a(0), isIp4(FALSE)
+{
+ a6 = a_;
+}
+
+/*!
+ \class TQHostAddress qhostaddress.h
+ \brief The TQHostAddress class provides an IP address.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ This class contains an IP address in a platform and protocol
+ independent manner. It stores both IPv4 and IPv6 addresses in a
+ way that you can easily access on any platform.
+
+ TQHostAddress is normally used with the classes TQSocket,
+ TQServerSocket and TQSocketDevice to set up a server or to connect
+ to a host.
+
+ Host addresses may be set with setAddress() and retrieved with
+ ip4Addr() or toString().
+
+ \sa TQSocket, TQServerSocket, TQSocketDevice
+*/
+
+
+/*!
+ Creates a host address object with the IP address 0.0.0.0.
+*/
+TQHostAddress::TQHostAddress()
+ : d( new TQHostAddressPrivate )
+{
+}
+
+
+/*!
+ Creates a host address object for the IPv4 address \a ip4Addr.
+*/
+TQHostAddress::TQHostAddress( Q_UINT32 ip4Addr )
+ : d( new TQHostAddressPrivate( ip4Addr ) )
+{
+}
+
+
+/*!
+ Creates a host address object with the specified IPv6 address.
+
+ \a ip6Addr must be a 16 byte array in network byte order
+ (high-order byte first).
+*/
+TQHostAddress::TQHostAddress( Q_UINT8 *ip6Addr )
+ : d( new TQHostAddressPrivate( ip6Addr ) )
+{
+}
+
+/*!
+ Creates a host address object with the IPv6 address, \a ip6Addr.
+*/
+TQHostAddress::TQHostAddress(const Q_IPV6ADDR &ip6Addr)
+ : d(new TQHostAddressPrivate(ip6Addr))
+{
+}
+
+// ### DOC: Can only make this public if we specify precisely the
+// format of the address string.
+/*!
+ \internal
+*/
+TQHostAddress::TQHostAddress(const TQString &address)
+ : d( new TQHostAddressPrivate )
+{
+ setAddress( address );
+}
+
+/*!
+ Creates a copy of \a address.
+*/
+TQHostAddress::TQHostAddress( const TQHostAddress &address )
+ : d( new TQHostAddressPrivate )
+{
+ *d = *(address.d);
+}
+
+
+/*!
+ Destroys the host address object.
+*/
+TQHostAddress::~TQHostAddress()
+{
+ delete d;
+}
+
+
+/*!
+ Assigns another host address object \a address to this object and
+ returns a reference to this object.
+*/
+TQHostAddress & TQHostAddress::operator=( const TQHostAddress & address )
+{
+ *d = *(address.d);
+ return *this;
+}
+
+
+/*!
+ Set the IPv4 address specified by \a ip4Addr.
+*/
+void TQHostAddress::setAddress( Q_UINT32 ip4Addr )
+{
+ delete d;
+ d = new TQHostAddressPrivate( ip4Addr );
+}
+
+
+/*!
+ \overload
+
+ Set the IPv6 address specified by \a ip6Addr.
+
+ \a ip6Addr must be a 16 byte array in network byte order
+ (high-order byte first).
+*/
+void TQHostAddress::setAddress( Q_UINT8 *ip6Addr )
+{
+ delete d;
+ d = new TQHostAddressPrivate( ip6Addr );
+}
+
+#ifndef QT_NO_STRINGLIST
+static bool parseIp4(const TQString& address, Q_UINT32 *addr)
+{
+ TQStringList ipv4 = TQStringList::split(".", address, FALSE);
+ if (ipv4.count() == 4) {
+ int i = 0;
+ bool ok = TRUE;
+ while(ok && i < 4) {
+ uint byteValue = ipv4[i].toUInt(&ok);
+ if (byteValue > 255)
+ ok = FALSE;
+ if (ok)
+ *addr = (*addr << 8) + byteValue;
+ ++i;
+ }
+ if (ok)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*!
+ \overload
+
+ Sets the IPv4 or IPv6 address specified by the string
+ representation \a address (e.g. "127.0.0.1"). Returns TRUE and
+ sets the address if the address was successfully parsed; otherwise
+ returns FALSE and leaves the address unchanged.
+*/
+bool TQHostAddress::setAddress(const TQString& address)
+{
+ TQString a = address.simplifyWhiteSpace();
+
+ // try ipv4
+ Q_UINT32 maybeIp4 = 0;
+ if (parseIp4(address, &maybeIp4)) {
+ setAddress(maybeIp4);
+ return TRUE;
+ }
+
+ // try ipv6
+ TQStringList ipv6 = TQStringList::split(":", a, TRUE);
+ int count = (int)ipv6.count();
+ if (count < 3)
+ return FALSE; // there must be at least two ":"
+ if (count > 8)
+ return FALSE; // maximum of seven ":" exceeded
+ Q_UINT8 maybeIp6[16];
+ int mc = 16;
+ int fillCount = 9 - count;
+ for (int i=count-1; i>=0; --i) {
+ if ( mc <= 0 )
+ return FALSE;
+
+ if (ipv6[i].isEmpty()) {
+ if (i==count-1) {
+ // special case: ":" is last character
+ if (!ipv6[i-1].isEmpty())
+ return FALSE;
+ maybeIp6[--mc] = 0;
+ maybeIp6[--mc] = 0;
+ } else if (i==0) {
+ // special case: ":" is first character
+ if (!ipv6[i+1].isEmpty())
+ return FALSE;
+ maybeIp6[--mc] = 0;
+ maybeIp6[--mc] = 0;
+ } else {
+ for (int j=0; j<fillCount; ++j) {
+ if ( mc <= 0 )
+ return FALSE;
+ maybeIp6[--mc] = 0;
+ maybeIp6[--mc] = 0;
+ }
+ }
+ } else {
+ bool ok = FALSE;
+ uint byteValue = ipv6[i].toUInt(&ok, 16);
+ if (ok && byteValue <= 0xffff) {
+ maybeIp6[--mc] = byteValue & 0xff;
+ maybeIp6[--mc] = (byteValue >> 8) & 0xff;
+ } else {
+ if (i == count-1) {
+ // parse the ipv4 part of a mixed type
+ if (!parseIp4(ipv6[i], &maybeIp4))
+ return FALSE;
+ maybeIp6[--mc] = maybeIp4 & 0xff;
+ maybeIp6[--mc] = (maybeIp4 >> 8) & 0xff;
+ maybeIp6[--mc] = (maybeIp4 >> 16) & 0xff;
+ maybeIp6[--mc] = (maybeIp4 >> 24) & 0xff;
+ --fillCount;
+ } else {
+ return FALSE;
+ }
+ }
+ }
+ }
+ if (mc == 0) {
+ setAddress(maybeIp6);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+#endif
+
+/*!
+ \obsolete
+
+ Use isIPv4Address() instead.
+*/
+bool TQHostAddress::isIp4Addr() const
+{
+ return isIPv4Address();
+}
+
+/*!
+ Returns TRUE if the host address represents an IPv4 address;
+ otherwise returns FALSE.
+*/
+bool TQHostAddress::isIPv4Address() const
+{
+ return d->isIp4;
+}
+
+/*!
+ \obsolete
+
+ Use toIPv4Address() instead.
+*/
+Q_UINT32 TQHostAddress::ip4Addr() const
+{
+ return toIPv4Address();
+}
+
+/*!
+ Returns the IPv4 address as a number.
+
+ For example, if the address is 127.0.0.1, the returned value is
+ 2130706433 (i.e. 0x7f000001).
+
+ This value is only valid when isIp4Addr() returns TRUE.
+
+ \sa toString()
+*/
+Q_UINT32 TQHostAddress::toIPv4Address() const
+{
+ return d->a;
+}
+
+/*!
+ Returns TRUE if the host address represents an IPv6 address;
+ otherwise returns FALSE.
+*/
+bool TQHostAddress::isIPv6Address() const
+{
+ return !d->isIp4;
+}
+
+/*!
+ Returns the IPv6 address as a Q_IPV6ADDR structure. The structure
+ consists of 16 unsigned characters.
+
+ \code
+ Q_IPV6ADDR addr = hostAddr.ip6Addr();
+ // addr.c[] contains 16 unsigned characters
+
+ for (int i = 0; i < 16; ++i) {
+ // process addr.c[i]
+ }
+ \endcode
+
+ This value is only valid when isIPv6Address() returns TRUE.
+
+ \sa toString()
+*/
+Q_IPV6ADDR TQHostAddress::toIPv6Address() const
+{
+ return d->a6;
+}
+
+#ifndef QT_NO_SPRINTF
+/*!
+ Returns the address as a string.
+
+ For example, if the address is the IPv4 address 127.0.0.1, the
+ returned string is "127.0.0.1".
+
+ \sa ip4Addr()
+*/
+TQString TQHostAddress::toString() const
+{
+ if ( d->isIp4 ) {
+ Q_UINT32 i = ip4Addr();
+ TQString s;
+ s.sprintf( "%d.%d.%d.%d", (i>>24) & 0xff, (i>>16) & 0xff,
+ (i >> 8) & 0xff, i & 0xff );
+ return s;
+ } else {
+ Q_UINT16 ugle[8];
+ for ( int i=0; i<8; i++ ) {
+ ugle[i] = ( (Q_UINT16)( d->a6.c[2*i] ) << 8 ) |
+ ( (Q_UINT16)( d->a6.c[2*i+1] ) );
+ }
+ TQString s;
+ s.sprintf( "%X:%X:%X:%X:%X:%X:%X:%X",
+ ugle[0], ugle[1], ugle[2], ugle[3],
+ ugle[4], ugle[5], ugle[6], ugle[7] );
+ return s;
+ }
+}
+#endif
+
+
+/*!
+ Returns TRUE if this host address is the same as \a other;
+ otherwise returns FALSE.
+*/
+bool TQHostAddress::operator==( const TQHostAddress & other ) const
+{
+ return d->a == other.d->a;
+}
+
+
+/*!
+ Returns TRUE if this host address is null (INADDR_ANY or in6addr_any). The
+ default constructor creates a null address, and that address isn't valid
+ for any particular host or interface.
+*/
+bool TQHostAddress::isNull() const
+{
+ if ( d->isIp4 )
+ return d->a == 0;
+ int i = 0;
+ while( i < 16 ) {
+ if ( d->a6.c[i++] != 0 )
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#endif //QT_NO_NETWORK
diff --git a/src/network/qhostaddress.h b/src/network/qhostaddress.h
new file mode 100644
index 00000000..99ea62f6
--- /dev/null
+++ b/src/network/qhostaddress.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Definition of TQHostAddress class.
+**
+** Created : 979899
+**
+** Copyright (C) 1997-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQHOSTADDRESS_H
+#define TQHOSTADDRESS_H
+
+#ifndef QT_H
+#include "qstring.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_NETWORK
+#else
+#define TQM_EXPORT_NETWORK Q_EXPORT
+#endif
+
+#ifndef QT_NO_NETWORK
+class TQHostAddressPrivate;
+
+typedef struct {
+ Q_UINT8 c[16];
+} Q_IPV6ADDR;
+
+class TQM_EXPORT_NETWORK TQHostAddress
+{
+public:
+ TQHostAddress();
+ TQHostAddress( Q_UINT32 ip4Addr );
+ TQHostAddress( Q_UINT8 *ip6Addr );
+ TQHostAddress(const Q_IPV6ADDR &ip6Addr);
+#ifndef QT_NO_STRINGLIST
+ TQHostAddress(const TQString &address);
+#endif
+ TQHostAddress( const TQHostAddress & );
+ virtual ~TQHostAddress();
+
+ TQHostAddress & operator=( const TQHostAddress & );
+
+ void setAddress( Q_UINT32 ip4Addr );
+ void setAddress( Q_UINT8 *ip6Addr );
+#ifndef QT_NO_STRINGLIST
+ bool setAddress( const TQString& address );
+#endif
+ bool isIp4Addr() const; // obsolete
+ Q_UINT32 ip4Addr() const; // obsolete
+
+ bool isIPv4Address() const;
+ Q_UINT32 toIPv4Address() const;
+ bool isIPv6Address() const;
+ Q_IPV6ADDR toIPv6Address() const;
+
+#ifndef QT_NO_SPRINTF
+ TQString toString() const;
+#endif
+
+ bool operator==( const TQHostAddress & ) const;
+ bool isNull() const;
+
+private:
+ TQHostAddressPrivate* d;
+};
+
+#endif //QT_NO_NETWORK
+#endif
diff --git a/src/network/qhttp.cpp b/src/network/qhttp.cpp
new file mode 100644
index 00000000..143421c5
--- /dev/null
+++ b/src/network/qhttp.cpp
@@ -0,0 +1,2384 @@
+/****************************************************************************
+**
+** Implementation of TQHttp and related classes.
+**
+** Created : 970521
+**
+** Copyright (C) 1997-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qhttp.h"
+
+#ifndef QT_NO_NETWORKPROTOCOL_HTTP
+
+#include "qsocket.h"
+#include "qtextstream.h"
+#include "qmap.h"
+#include "qstring.h"
+#include "qstringlist.h"
+#include "qcstring.h"
+#include "qbuffer.h"
+#include "qurloperator.h"
+#include "qtimer.h"
+#include "private/qinternal_p.h"
+
+//#define TQHTTP_DEBUG
+
+class TQHttpPrivate
+{
+public:
+ TQHttpPrivate() :
+ state( TQHttp::Unconnected ),
+ error( TQHttp::NoError ),
+ hostname( TQString::null ),
+ port( 0 ),
+ toDevice( 0 ),
+ postDevice( 0 ),
+ bytesDone( 0 ),
+ chunkedSize( -1 ),
+ idleTimer( 0 )
+ {
+ pending.setAutoDelete( TRUE );
+ }
+
+ TQSocket socket;
+ TQPtrList<TQHttpRequest> pending;
+
+ TQHttp::State state;
+ TQHttp::Error error;
+ TQString errorString;
+
+ TQString hostname;
+ Q_UINT16 port;
+
+ TQByteArray buffer;
+ TQIODevice* toDevice;
+ TQIODevice* postDevice;
+
+ uint bytesDone;
+ uint bytesTotal;
+ Q_LONG chunkedSize;
+
+ TQHttpRequestHeader header;
+
+ bool readHeader;
+ TQString headerStr;
+ TQHttpResponseHeader response;
+
+ int idleTimer;
+
+ TQMembuf rba;
+};
+
+class TQHttpRequest
+{
+public:
+ TQHttpRequest()
+ {
+ id = ++idCounter;
+ }
+ virtual ~TQHttpRequest()
+ { }
+
+ virtual void start( TQHttp * ) = 0;
+ virtual bool hasRequestHeader();
+ virtual TQHttpRequestHeader requestHeader();
+
+ virtual TQIODevice* sourceDevice() = 0;
+ virtual TQIODevice* destinationDevice() = 0;
+
+ int id;
+
+private:
+ static int idCounter;
+};
+
+int TQHttpRequest::idCounter = 0;
+
+bool TQHttpRequest::hasRequestHeader()
+{
+ return FALSE;
+}
+
+TQHttpRequestHeader TQHttpRequest::requestHeader()
+{
+ return TQHttpRequestHeader();
+}
+
+/****************************************************
+ *
+ * TQHttpNormalRequest
+ *
+ ****************************************************/
+
+class TQHttpNormalRequest : public TQHttpRequest
+{
+public:
+ TQHttpNormalRequest( const TQHttpRequestHeader &h, TQIODevice *d, TQIODevice *t ) :
+ header(h), to(t)
+ {
+ is_ba = FALSE;
+ data.dev = d;
+ }
+
+ TQHttpNormalRequest( const TQHttpRequestHeader &h, TQByteArray *d, TQIODevice *t ) :
+ header(h), to(t)
+ {
+ is_ba = TRUE;
+ data.ba = d;
+ }
+
+ ~TQHttpNormalRequest()
+ {
+ if ( is_ba )
+ delete data.ba;
+ }
+
+ void start( TQHttp * );
+ bool hasRequestHeader();
+ TQHttpRequestHeader requestHeader();
+
+ TQIODevice* sourceDevice();
+ TQIODevice* destinationDevice();
+
+protected:
+ TQHttpRequestHeader header;
+
+private:
+ union {
+ TQByteArray *ba;
+ TQIODevice *dev;
+ } data;
+ bool is_ba;
+ TQIODevice *to;
+};
+
+void TQHttpNormalRequest::start( TQHttp *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 = TQByteArray();
+
+ 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 TQHttpNormalRequest::hasRequestHeader()
+{
+ return TRUE;
+}
+
+TQHttpRequestHeader TQHttpNormalRequest::requestHeader()
+{
+ return header;
+}
+
+TQIODevice* TQHttpNormalRequest::sourceDevice()
+{
+ if ( is_ba )
+ return 0;
+ return data.dev;
+}
+
+TQIODevice* TQHttpNormalRequest::destinationDevice()
+{
+ return to;
+}
+
+/****************************************************
+ *
+ * TQHttpPGHRequest
+ * (like a TQHttpNormalRequest, but for the convenience
+ * functions put(), get() and head() -- i.e. set the
+ * host header field correctly before sending the
+ * request)
+ *
+ ****************************************************/
+
+class TQHttpPGHRequest : public TQHttpNormalRequest
+{
+public:
+ TQHttpPGHRequest( const TQHttpRequestHeader &h, TQIODevice *d, TQIODevice *t ) :
+ TQHttpNormalRequest( h, d, t )
+ { }
+
+ TQHttpPGHRequest( const TQHttpRequestHeader &h, TQByteArray *d, TQIODevice *t ) :
+ TQHttpNormalRequest( h, d, t )
+ { }
+
+ ~TQHttpPGHRequest()
+ { }
+
+ void start( TQHttp * );
+};
+
+void TQHttpPGHRequest::start( TQHttp *http )
+{
+ header.setValue( "Host", http->d->hostname );
+ TQHttpNormalRequest::start( http );
+}
+
+/****************************************************
+ *
+ * TQHttpSetHostRequest
+ *
+ ****************************************************/
+
+class TQHttpSetHostRequest : public TQHttpRequest
+{
+public:
+ TQHttpSetHostRequest( const TQString &h, Q_UINT16 p ) :
+ hostname(h), port(p)
+ { }
+
+ void start( TQHttp * );
+
+ TQIODevice* sourceDevice()
+ { return 0; }
+ TQIODevice* destinationDevice()
+ { return 0; }
+
+private:
+ TQString hostname;
+ Q_UINT16 port;
+};
+
+void TQHttpSetHostRequest::start( TQHttp *http )
+{
+ http->d->hostname = hostname;
+ http->d->port = port;
+ http->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * TQHttpCloseRequest
+ *
+ ****************************************************/
+
+class TQHttpCloseRequest : public TQHttpRequest
+{
+public:
+ TQHttpCloseRequest()
+ { }
+ void start( TQHttp * );
+
+ TQIODevice* sourceDevice()
+ { return 0; }
+ TQIODevice* destinationDevice()
+ { return 0; }
+};
+
+void TQHttpCloseRequest::start( TQHttp *http )
+{
+ http->close();
+}
+
+/****************************************************
+ *
+ * TQHttpHeader
+ *
+ ****************************************************/
+
+/*!
+ \class TQHttpHeader qhttp.h
+ \brief The TQHttpHeader class contains header information for HTTP.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ In most cases you should use the more specialized derivatives of
+ this class, TQHttpResponseHeader and TQHttpRequestHeader, rather
+ than directly using TQHttpHeader.
+
+ TQHttpHeader 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:
+ \code
+ content-type: text/html
+ \endcode
+
+ 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.
+ \code
+ header.setValue( "content-type", "text/html" );
+ TQString contentType = header.value( "content-type" );
+ \endcode
+
+ 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 TQHttpRequestHeader TQHttpResponseHeader
+*/
+
+/*!
+ \fn int TQHttpHeader::majorVersion() const
+
+ Returns the major protocol-version of the HTTP header.
+*/
+
+/*!
+ \fn int TQHttpHeader::minorVersion() const
+
+ Returns the minor protocol-version of the HTTP header.
+*/
+
+/*!
+ Constructs an empty HTTP header.
+*/
+TQHttpHeader::TQHttpHeader()
+ : valid( TRUE )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+TQHttpHeader::TQHttpHeader( const TQHttpHeader& 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.
+*/
+TQHttpHeader::TQHttpHeader( const TQString& str )
+ : valid( TRUE )
+{
+ parse( str );
+}
+
+/*!
+ Destructor.
+*/
+TQHttpHeader::~TQHttpHeader()
+{
+}
+
+/*!
+ Assigns \a h and returns a reference to this http header.
+*/
+TQHttpHeader& TQHttpHeader::operator=( const TQHttpHeader& h )
+{
+ values = h.values;
+ valid = h.valid;
+ return *this;
+}
+
+/*!
+ Returns TRUE if the HTTP header is valid; otherwise returns FALSE.
+
+ A TQHttpHeader is invalid if it was created by parsing a malformed string.
+*/
+bool TQHttpHeader::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 TQHttpHeader becomes \link isValid() invalid\endlink.
+
+ Returns TRUE if \a str was successfully parsed; otherwise returns FALSE.
+
+ \sa toString()
+*/
+bool TQHttpHeader::parse( const TQString& str )
+{
+ TQStringList lst;
+ int pos = str.find( '\n' );
+ if ( pos > 0 && str.at( pos - 1 ) == '\r' )
+ lst = TQStringList::split( "\r\n", str.stripWhiteSpace(), FALSE );
+ else
+ lst = TQStringList::split( "\n", str.stripWhiteSpace(), FALSE );
+
+ if ( lst.isEmpty() )
+ return TRUE;
+
+ TQStringList lines;
+ TQStringList::Iterator it = lst.begin();
+ for( ; it != lst.end(); ++it ) {
+ if ( !(*it).isEmpty() ) {
+ if ( (*it)[0].isSpace() ) {
+ if ( !lines.isEmpty() ) {
+ lines.last() += " ";
+ 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 TQHttpHeader::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()
+*/
+TQString TQHttpHeader::value( const TQString& key ) const
+{
+ return values[ key.lower() ];
+}
+
+/*!
+ Returns a list of the keys in the HTTP header.
+
+ \sa hasKey()
+*/
+TQStringList TQHttpHeader::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 TQHttpHeader::hasKey( const TQString& 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 TQHttpHeader::setValue( const TQString& key, const TQString& value )
+{
+ values[ key.lower() ] = value;
+}
+
+/*!
+ Removes the entry with the key \a key from the HTTP header.
+
+ \sa value() setValue()
+*/
+void TQHttpHeader::removeValue( const TQString& 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 TQHttpHeader::parseLine( const TQString& line, int )
+{
+ int i = line.find( ":" );
+ 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
+ TQString. It consists of lines with the format: key, colon, space,
+ value, "\r\n".
+*/
+TQString TQHttpHeader::toString() const
+{
+ if ( !isValid() )
+ return "";
+
+ TQString ret = "";
+
+ TQMap<TQString,TQString>::ConstIterator it = values.begin();
+ for( ; it != values.end(); ++it )
+ ret += it.key() + ": " + it.data() + "\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 TQHttpHeader::hasContentLength() const
+{
+ return hasKey( "content-length" );
+}
+
+/*!
+ Returns the value of the special HTTP header field \c
+ content-length.
+
+ \sa setContentLength() hasContentLength()
+*/
+uint TQHttpHeader::contentLength() const
+{
+ return values[ "content-length" ].toUInt();
+}
+
+/*!
+ Sets the value of the special HTTP header field \c content-length
+ to \a len.
+
+ \sa contentLength() hasContentLength()
+*/
+void TQHttpHeader::setContentLength( int len )
+{
+ values[ "content-length" ] = TQString::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 TQHttpHeader::hasContentType() const
+{
+ return hasKey( "content-type" );
+}
+
+/*!
+ Returns the value of the special HTTP header field \c content-type.
+
+ \sa setContentType() hasContentType()
+*/
+TQString TQHttpHeader::contentType() const
+{
+ TQString type = values[ "content-type" ];
+ if ( type.isEmpty() )
+ return TQString::null;
+
+ int pos = type.find( ";" );
+ 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 TQHttpHeader::setContentType( const TQString& type )
+{
+ values[ "content-type" ] = type;
+}
+
+/****************************************************
+ *
+ * TQHttpResponseHeader
+ *
+ ****************************************************/
+
+/*!
+ \class TQHttpResponseHeader qhttp.h
+ \brief The TQHttpResponseHeader class contains response header information for HTTP.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ This class is used by the TQHttp 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 TQHttpRequestHeader TQHttp
+*/
+
+/*!
+ Constructs an empty HTTP response header.
+*/
+TQHttpResponseHeader::TQHttpResponseHeader()
+{
+ 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.
+*/
+TQHttpResponseHeader::TQHttpResponseHeader( int code, const TQString& text, int majorVer, int minorVer )
+ : TQHttpHeader(), statCode( code ), reasonPhr( text ), majVer( majorVer ), minVer( minorVer )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+TQHttpResponseHeader::TQHttpResponseHeader( const TQHttpResponseHeader& header )
+ : TQHttpHeader( 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.
+*/
+TQHttpResponseHeader::TQHttpResponseHeader( const TQString& str )
+ : TQHttpHeader()
+{
+ 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 TQHttpResponseHeader::setStatusLine( int code, const TQString& 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 TQHttpResponseHeader::statusCode() const
+{
+ return statCode;
+}
+
+/*!
+ Returns the reason phrase of the HTTP response header.
+
+ \sa statusCode() majorVersion() minorVersion()
+*/
+TQString TQHttpResponseHeader::reasonPhrase() const
+{
+ return reasonPhr;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP response header.
+
+ \sa minorVersion() statusCode() reasonPhrase()
+*/
+int TQHttpResponseHeader::majorVersion() const
+{
+ return majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP response header.
+
+ \sa majorVersion() statusCode() reasonPhrase()
+*/
+int TQHttpResponseHeader::minorVersion() const
+{
+ return minVer;
+}
+
+/*! \reimp
+*/
+bool TQHttpResponseHeader::parseLine( const TQString& line, int number )
+{
+ if ( number != 0 )
+ return TQHttpHeader::parseLine( line, number );
+
+ TQString l = line.simplifyWhiteSpace();
+ if ( l.length() < 10 )
+ return FALSE;
+
+ if ( l.left( 5 ) == "HTTP/" && l[5].isDigit() && l[6] == '.' &&
+ l[7].isDigit() && l[8] == ' ' && l[9].isDigit() ) {
+ majVer = l[5].latin1() - '0';
+ minVer = l[7].latin1() - '0';
+
+ int pos = l.find( ' ', 9 );
+ if ( pos != -1 ) {
+ reasonPhr = l.mid( pos + 1 );
+ statCode = l.mid( 9, pos - 9 ).toInt();
+ } else {
+ statCode = l.mid( 9 ).toInt();
+ reasonPhr = TQString::null;
+ }
+ } else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*! \reimp
+*/
+TQString TQHttpResponseHeader::toString() const
+{
+ TQString ret( "HTTP/%1.%2 %3 %4\r\n%5\r\n" );
+ return ret.arg( majVer ).arg ( minVer ).arg( statCode ).arg( reasonPhr ).arg( TQHttpHeader::toString() );
+}
+
+/****************************************************
+ *
+ * TQHttpRequestHeader
+ *
+ ****************************************************/
+
+/*!
+ \class TQHttpRequestHeader qhttp.h
+ \brief The TQHttpRequestHeader class contains request header information for
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+ HTTP.
+\if defined(commercial_edition)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ This class is used in the TQHttp 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 TQHttpHeader subclass so that class's functions,
+ e.g. \link TQHttpHeader::setValue() setValue()\endlink, \link
+ TQHttpHeader::value() value()\endlink, etc. are also available.
+
+ \sa TQHttpResponseHeader TQHttp
+
+ \important value() setValue()
+*/
+
+/*!
+ Constructs an empty HTTP request header.
+*/
+TQHttpRequestHeader::TQHttpRequestHeader()
+ : TQHttpHeader()
+{
+ 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.
+*/
+TQHttpRequestHeader::TQHttpRequestHeader( const TQString& method, const TQString& path, int majorVer, int minorVer )
+ : TQHttpHeader(), m( method ), p( path ), majVer( majorVer ), minVer( minorVer )
+{
+}
+
+/*!
+ Constructs a copy of \a header.
+*/
+TQHttpRequestHeader::TQHttpRequestHeader( const TQHttpRequestHeader& header )
+ : TQHttpHeader( 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.
+*/
+TQHttpRequestHeader::TQHttpRequestHeader( const TQString& str )
+ : TQHttpHeader()
+{
+ 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 TQHttpRequestHeader::setRequest( const TQString& method, const TQString& 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()
+*/
+TQString TQHttpRequestHeader::method() const
+{
+ return m;
+}
+
+/*!
+ Returns the request-URI of the HTTP request header.
+
+ \sa method() majorVersion() minorVersion() setRequest()
+*/
+TQString TQHttpRequestHeader::path() const
+{
+ return p;
+}
+
+/*!
+ Returns the major protocol-version of the HTTP request header.
+
+ \sa minorVersion() method() path() setRequest()
+*/
+int TQHttpRequestHeader::majorVersion() const
+{
+ return majVer;
+}
+
+/*!
+ Returns the minor protocol-version of the HTTP request header.
+
+ \sa majorVersion() method() path() setRequest()
+*/
+int TQHttpRequestHeader::minorVersion() const
+{
+ return minVer;
+}
+
+/*! \reimp
+*/
+bool TQHttpRequestHeader::parseLine( const TQString& line, int number )
+{
+ if ( number != 0 )
+ return TQHttpHeader::parseLine( line, number );
+
+ TQStringList lst = TQStringList::split( " ", line.simplifyWhiteSpace() );
+ if ( lst.count() > 0 ) {
+ m = lst[0];
+ if ( lst.count() > 1 ) {
+ p = lst[1];
+ if ( lst.count() > 2 ) {
+ TQString v = lst[2];
+ if ( v.length() >= 8 && v.left( 5 ) == "HTTP/" &&
+ v[5].isDigit() && v[6] == '.' && v[7].isDigit() ) {
+ majVer = v[5].latin1() - '0';
+ minVer = v[7].latin1() - '0';
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*! \reimp
+*/
+TQString TQHttpRequestHeader::toString() const
+{
+ TQString first( "%1 %2");
+ TQString last(" HTTP/%3.%4\r\n%5\r\n" );
+ return first.arg( m ).arg( p ) +
+ last.arg( majVer ).arg( minVer ).arg( TQHttpHeader::toString());
+}
+
+
+/****************************************************
+ *
+ * TQHttp
+ *
+ ****************************************************/
+/*!
+ \class TQHttp qhttp.h
+ \brief The TQHttp class provides an implementation of the HTTP protocol.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ This class provides two different interfaces: one is the
+ TQNetworkProtocol interface that allows you to use HTTP through the
+ TQUrlOperator 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 TQHttp with the TQNetworkProtocol interface, you
+ do not use it directly, but rather through a TQUrlOperator, for
+ example:
+
+ \code
+ TQUrlOperator op( "http://www.trolltech.com" );
+ op.get( "index.html" );
+ \endcode
+
+ This code will only work if the TQHttp class is registered; to
+ register the class, you must call qInitNetworkProtocols() before
+ using a TQUrlOperator with HTTP.
+
+ The TQNetworkProtocol interface for HTTP only supports the
+ operations operationGet() and operationPut(), i.e.
+ TQUrlOperator::get() and TQUrlOperator::put(), if you use it with a
+ TQUrlOperator.
+
+ 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://www.trolltech.com/index.html):
+
+ \code
+ TQHttpRequestHeader header( "GET", "/index.html" );
+ header.setValue( "Host", "www.trolltech.com" );
+ http->setHost( "www.trolltech.com" );
+ http->request( header );
+ \endcode
+
+ For the common HTTP requests \c GET, \c POST and \c HEAD, TQHttp
+ 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:
+
+ \code
+ http->setHost( "www.trolltech.com" ); // id == 1
+ http->get( "/index.html" ); // id == 2
+ \endcode
+
+ For this example the following sequence of signals is emitted
+ (with small variations, depending on network traffic, etc.):
+
+ \code
+ requestStarted( 1 )
+ requestFinished( 1, FALSE )
+
+ requestStarted( 2 )
+ stateChanged( Connecting )
+ stateChanged( Sending )
+ dataSendProgress( 77, 77 )
+ stateChanged( Reading )
+ responseHeaderReceived( responseheader )
+ dataReadProgress( 5388, 0 )
+ readyRead( responseheader )
+ dataReadProgress( 18300, 0 )
+ readyRead( responseheader )
+ stateChanged( Connected )
+ requestFinished( 2, FALSE )
+
+ done( FALSE )
+
+ stateChanged( Closing )
+ stateChanged( Unconnected )
+ \endcode
+
+ The dataSendProgress() and dataReadProgress() signals in the above
+ example are useful if you want to show a \link TQProgressBar
+ progressbar\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 TQProgressBar 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
+
+ \code
+ http->setHost( "www.foo.bar" ); // id == 1
+ http->get( "/index.html" ); // id == 2
+ http->post( "register.html", data ); // id == 3
+ \endcode
+
+ and the get() request fails because the host lookup fails, then
+ the post() request is never executed and the signals would look
+ like this:
+
+ \code
+ requestStarted( 1 )
+ requestFinished( 1, FALSE )
+
+ requestStarted( 2 )
+ stateChanged( HostLookup )
+ requestFinished( 2, TRUE )
+
+ done( TRUE )
+
+ stateChanged( Unconnected )
+ \endcode
+
+ 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
+ TQHttpResponseHeader::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 \link network.html TQt Network Documentation \endlink TQNetworkProtocol, TQUrlOperator TQFtp
+*/
+
+/*!
+ Constructs a TQHttp object.
+*/
+TQHttp::TQHttp()
+{
+ init();
+}
+
+/*!
+ Constructs a TQHttp object. The parameters \a parent and \a name
+ are passed on to the TQObject constructor.
+*/
+TQHttp::TQHttp( TQObject* parent, const char* name )
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+}
+
+/*!
+ Constructs a TQHttp 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 TQObject
+ constructor.
+
+ \sa setHost()
+*/
+TQHttp::TQHttp( const TQString &hostname, Q_UINT16 port, TQObject* parent, const char* name )
+{
+ if ( parent )
+ parent->insertChild( this );
+ setName( name );
+ init();
+
+ d->hostname = hostname;
+ d->port = port;
+}
+
+void TQHttp::init()
+{
+ bytesRead = 0;
+ d = new TQHttpPrivate;
+ d->errorString = 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 TQHttp object. If there is an open connection, it is
+ closed.
+*/
+TQHttp::~TQHttp()
+{
+ abort();
+ delete d;
+}
+
+/*!
+ \enum TQHttp::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 TQHttp::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 TQHttp::stateChanged( int state )
+
+ This signal is emitted when the state of the TQHttp 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 TQHttp::responseHeaderReceived( const TQHttpResponseHeader& 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 TQHttp::readyRead( const TQHttpResponseHeader& 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 TQHttp::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 TQProgressBar, 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() TQProgressBar::setProgress()
+*/
+
+/*!
+ \fn void TQHttp::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 TQProgressBar, 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() TQProgressBar::setProgress()
+*/
+
+/*!
+ \fn void TQHttp::requestStarted( int id )
+
+ This signal is emitted when processing the request identified by
+ \a id starts.
+
+ \sa requestFinished() done()
+*/
+
+/*!
+ \fn void TQHttp::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 TQHttp::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 TQHttp::abort()
+{
+ TQHttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ finishedWithError( 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 TQHttp::bytesAvailable() const
+{
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp::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 TQHttp::readBlock( char *data, Q_ULONG maxlen )
+{
+ if ( data == 0 && maxlen != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "TQHttp::readBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( maxlen >= d->rba.size() )
+ maxlen = d->rba.size();
+ d->rba.consumeBytes( maxlen, data );
+
+ d->bytesDone += maxlen;
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp::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()
+*/
+TQByteArray TQHttp::readAll()
+{
+ Q_ULONG avail = bytesAvailable();
+ TQByteArray 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 TQHttp::currentId() const
+{
+ TQHttpRequest *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.
+ TQHttpRequestHeader::isValid() returns FALSE.
+
+ \sa currentId()
+*/
+TQHttpRequestHeader TQHttp::currentRequest() const
+{
+ TQHttpRequest *r = d->pending.getFirst();
+ if ( r != 0 && r->hasRequestHeader() )
+ return r->requestHeader();
+ return TQHttpRequestHeader();
+}
+
+/*!
+ Returns the TQIODevice 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 TQIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentDestinationDevice() post() request()
+*/
+TQIODevice* TQHttp::currentSourceDevice() const
+{
+ TQHttpRequest *r = d->pending.getFirst();
+ if ( !r )
+ return 0;
+ return r->sourceDevice();
+}
+
+/*!
+ Returns the TQIODevice 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 TQIODevice in the slot connected to
+ the requestFinished() signal.
+
+ \sa currentDestinationDevice() get() post() request()
+*/
+TQIODevice* TQHttp::currentDestinationDevice() const
+{
+ TQHttpRequest *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 TQHttp::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 this as well, use abort().
+
+ \sa hasPendingRequests() abort()
+*/
+void TQHttp::clearPendingRequests()
+{
+ TQHttpRequest *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 TQHttp::setHost(const TQString &hostname, Q_UINT16 port )
+{
+ return addRequest( new TQHttpSetHostRequest( 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://www.trolltech.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 TQHttp::get( const TQString& path, TQIODevice* to )
+{
+ TQHttpRequestHeader header( "GET", path );
+ header.setValue( "Connection", "Keep-Alive" );
+ return addRequest( new TQHttpPGHRequest( header, (TQIODevice*)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://www.trolltech.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 TQHttp::post( const TQString& path, TQIODevice* data, TQIODevice* to )
+{
+ TQHttpRequestHeader header( "POST", path );
+ header.setValue( "Connection", "Keep-Alive" );
+ return addRequest( new TQHttpPGHRequest( header, data, to ) );
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int TQHttp::post( const TQString& path, const TQByteArray& data, TQIODevice* to )
+{
+ TQHttpRequestHeader header( "POST", path );
+ header.setValue( "Connection", "Keep-Alive" );
+ return addRequest( new TQHttpPGHRequest( header, new TQByteArray(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://www.trolltech.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 TQHttp::head( const TQString& path )
+{
+ TQHttpRequestHeader header( "HEAD", path );
+ header.setValue( "Connection", "Keep-Alive" );
+ return addRequest( new TQHttpPGHRequest( header, (TQIODevice*)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 TQHttp::request( const TQHttpRequestHeader &header, TQIODevice *data, TQIODevice *to )
+{
+ return addRequest( new TQHttpNormalRequest( header, data, to ) );
+}
+
+/*!
+ \overload
+
+ \a data is used as the content data of the HTTP request.
+*/
+int TQHttp::request( const TQHttpRequestHeader &header, const TQByteArray &data, TQIODevice *to )
+{
+ return addRequest( new TQHttpNormalRequest( header, new TQByteArray(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(), TQHttp sets
+ the connection to be keep-alive. You can also do this using the
+ header you pass to the request() function. TQHttp only closes the
+ connection to the HTTP server if the response header retquires 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 TQHttp::closeConnection()
+{
+ return addRequest( new TQHttpCloseRequest() );
+}
+
+int TQHttp::addRequest( TQHttpRequest *req )
+{
+ d->pending.append( req );
+
+ if ( d->pending.count() == 1 )
+ // don't emit the requestStarted() signal before the id is returned
+ TQTimer::singleShot( 0, this, SLOT(startNextRequest()) );
+
+ return req->id;
+}
+
+void TQHttp::startNextRequest()
+{
+ TQHttpRequest *r = d->pending.getFirst();
+ if ( r == 0 )
+ return;
+
+ d->error = NoError;
+ d->errorString = tr( "Unknown error" );
+
+ if ( bytesAvailable() )
+ readAll(); // clear the data
+ emit requestStarted( r->id );
+ r->start( this );
+}
+
+void TQHttp::sendRequest()
+{
+ if ( d->hostname.isNull() ) {
+ finishedWithError( 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() != TQSocket::Connection ) {
+ setState( TQHttp::Connecting );
+ d->socket.connectToHost( d->hostname, d->port );
+ } else {
+ slotConnected();
+ }
+
+}
+
+void TQHttp::finishedWithSuccess()
+{
+ TQHttpRequest *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 TQHttp::finishedWithError( const TQString& detail, int errorCode )
+{
+ TQHttpRequest *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 TQHttp::slotClosed()
+{
+ if ( d->state == Closing )
+ return;
+
+ if ( d->state == Reading ) {
+ if ( d->response.hasKey( "content-length" ) ) {
+ // We got Content-Length, so did we get all bytes?
+ if ( d->bytesDone+bytesAvailable() != d->response.contentLength() ) {
+ finishedWithError( tr("Wrong content length"), WrongContentLength );
+ }
+ }
+ } else if ( d->state == Connecting || d->state == Sending ) {
+ finishedWithError( tr("Server closed connection unexpectedly"), UnexpectedClose );
+ }
+
+ d->postDevice = 0;
+ setState( Closing );
+ d->idleTimer = startTimer( 0 );
+}
+
+void TQHttp::slotConnected()
+{
+ if ( d->state != Sending ) {
+ d->bytesDone = 0;
+ setState( Sending );
+ }
+
+ TQString str = d->header.toString();
+ d->bytesTotal = str.length();
+ d->socket.writeBlock( str.latin1(), d->bytesTotal );
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp: 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 = TQByteArray(); // save memory
+ }
+}
+
+void TQHttp::slotError( int err )
+{
+ d->postDevice = 0;
+
+ if ( d->state == Connecting || d->state == Reading || d->state == Sending ) {
+ switch ( err ) {
+ case TQSocket::ErrConnectionRefused:
+ finishedWithError( tr("Connection refused"), ConnectionRefused );
+ break;
+ case TQSocket::ErrHostNotFound:
+ finishedWithError( tr("Host %1 not found").arg(d->socket.peerName()), HostNotFound );
+ break;
+ default:
+ finishedWithError( tr("HTTP request failed"), UnknownError );
+ break;
+ }
+ }
+
+ close();
+}
+
+void TQHttp::slotBytesWritten( int written )
+{
+ d->bytesDone += written;
+ emit dataSendProgress( d->bytesDone, d->bytesTotal );
+
+ if ( !d->postDevice )
+ return;
+
+ if ( d->socket.bytesToWrite() == 0 ) {
+ int max = TQMIN( 4096, d->postDevice->size() - d->postDevice->at() );
+ TQByteArray 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 TQHttp::slotReadyRead()
+{
+ if ( d->state != Reading ) {
+ setState( Reading );
+ d->buffer = TQByteArray();
+ d->readHeader = TRUE;
+ d->headerStr = "";
+ d->bytesDone = 0;
+ d->chunkedSize = -1;
+ }
+
+ while ( d->readHeader ) {
+ bool end = FALSE;
+ TQString tmp;
+ while ( !end && d->socket.canReadLine() ) {
+ tmp = d->socket.readLine();
+ if ( tmp == "\r\n" || tmp == "\n" )
+ end = TRUE;
+ else
+ d->headerStr += tmp;
+ }
+
+ if ( !end )
+ return;
+
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp: read response header:\n---{\n%s}---", d->headerStr.latin1() );
+#endif
+ d->response = TQHttpResponseHeader( d->headerStr );
+ d->headerStr = "";
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp: read response header:\n---{\n%s}---", d->response.toString().latin1() );
+#endif
+ // Check header
+ if ( !d->response.isValid() ) {
+ finishedWithError( 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( "transfer-encoding" ) &&
+ d->response.value( "transfer-encoding" ).lower().contains( "chunked" ) )
+ d->chunkedSize = 0;
+
+ emit responseHeaderReceived( d->response );
+ }
+ }
+
+ if ( !d->readHeader ) {
+ bool everythingRead = FALSE;
+
+ if ( currentRequest().method() == "HEAD" ) {
+ everythingRead = TRUE;
+ } else {
+ Q_ULONG n = d->socket.bytesAvailable();
+ TQByteArray *arr = 0;
+ if ( d->chunkedSize != -1 ) {
+ // transfer-encoding is chunked
+ for ( ;; ) {
+ // get chunk size
+ if ( d->chunkedSize == 0 ) {
+ if ( !d->socket.canReadLine() )
+ break;
+ TQString sizeString = d->socket.readLine();
+ int tPos = sizeString.find( ';' );
+ if ( tPos != -1 )
+ sizeString.truncate( tPos );
+ bool ok;
+ d->chunkedSize = sizeString.toInt( &ok, 16 );
+ if ( !ok ) {
+ finishedWithError( 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() ) {
+ TQString read = d->socket.readLine();
+ if ( read == "\r\n" || read == "\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 = TQMIN( (Q_LONG)n, (d->chunkedSize < 0 ? (Q_LONG)n : d->chunkedSize) );
+ if ( !arr )
+ arr = new TQByteArray( 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( tr("Invalid HTTP chunked body"), WrongContentLength );
+ close();
+ return;
+ }
+ }
+ }
+ } else if ( d->response.hasContentLength() ) {
+ n = TQMIN( d->response.contentLength() - d->bytesDone, n );
+ if ( n > 0 ) {
+ arr = new TQByteArray( 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
+ TQByteArray temp = d->socket.readAll();
+ arr = new TQByteArray( temp );
+ }
+
+ if ( arr ) {
+ n = arr->size();
+ if ( d->toDevice ) {
+ d->toDevice->writeBlock( arr->data(), n );
+ delete arr;
+ d->bytesDone += n;
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp::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(TQHTTP_DEBUG)
+ qDebug( "TQHttp::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("connection").lower() == "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()
+*/
+TQHttp::State TQHttp::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.
+*/
+TQHttp::Error TQHttp::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.
+*/
+TQString TQHttp::errorString() const
+{
+ return d->errorString;
+}
+
+/*! \reimp
+*/
+void TQHttp::timerEvent( TQTimerEvent *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 {
+ TQObject::timerEvent( e );
+ }
+}
+
+void TQHttp::killIdleTimer()
+{
+ killTimer( d->idleTimer );
+ d->idleTimer = 0;
+}
+
+void TQHttp::setState( int s )
+{
+#if defined(TQHTTP_DEBUG)
+ qDebug( "TQHttp state changed %d -> %d", d->state, s );
+#endif
+ d->state = (State)s;
+ emit stateChanged( s );
+}
+
+void TQHttp::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() == TQSocket::Idle ) {
+ // Prepare to emit the requestFinished() signal.
+ d->idleTimer = startTimer( 0 );
+ }
+ }
+}
+
+/**********************************************************************
+ *
+ * TQHttp implementation of the TQNetworkProtocol interface
+ *
+ *********************************************************************/
+/*! \reimp
+*/
+int TQHttp::supportedOperations() const
+{
+ return OpGet | OpPut;
+}
+
+/*! \reimp
+*/
+void TQHttp::operationGet( TQNetworkOperation *op )
+{
+ connect( this, SIGNAL(readyRead(const TQHttpResponseHeader&)),
+ this, SLOT(clientReply(const TQHttpResponseHeader&)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ bytesRead = 0;
+ op->setState( StInProgress );
+ TQUrl u( operationInProgress()->arg( 0 ) );
+ TQHttpRequestHeader header( "GET", u.encodedPathAndQuery(), 1, 0 );
+ header.setValue( "Host", u.host() );
+ setHost( u.host(), u.port() != -1 ? u.port() : 80 );
+ request( header );
+}
+
+/*! \reimp
+*/
+void TQHttp::operationPut( TQNetworkOperation *op )
+{
+ connect( this, SIGNAL(readyRead(const TQHttpResponseHeader&)),
+ this, SLOT(clientReply(const TQHttpResponseHeader&)) );
+ connect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ connect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ bytesRead = 0;
+ op->setState( StInProgress );
+ TQUrl u( operationInProgress()->arg( 0 ) );
+ TQHttpRequestHeader header( "POST", u.encodedPathAndQuery(), 1, 0 );
+ header.setValue( "Host", u.host() );
+ setHost( u.host(), u.port() != -1 ? u.port() : 80 );
+ request( header, op->rawArg(1) );
+}
+
+void TQHttp::clientReply( const TQHttpResponseHeader &rep )
+{
+ TQNetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( rep.statusCode() >= 400 && rep.statusCode() < 600 ) {
+ op->setState( StFailed );
+ op->setProtocolDetail(
+ TQString("%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 ) {
+ TQByteArray ba = readAll();
+ emit data( ba, op );
+ bytesRead += ba.size();
+ if ( rep.hasContentLength() ) {
+ emit dataTransferProgress( bytesRead, rep.contentLength(), op );
+ }
+ }
+ }
+}
+
+void TQHttp::clientDone( bool err )
+{
+ disconnect( this, SIGNAL(readyRead(const TQHttpResponseHeader&)),
+ this, SLOT(clientReply(const TQHttpResponseHeader&)) );
+ disconnect( this, SIGNAL(done(bool)),
+ this, SLOT(clientDone(bool)) );
+ disconnect( this, SIGNAL(stateChanged(int)),
+ this, SLOT(clientStateChanged(int)) );
+
+ if ( err ) {
+ TQNetworkOperation *op = operationInProgress();
+ if ( op ) {
+ op->setState( TQNetworkProtocol::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 {
+ TQNetworkOperation *op = operationInProgress();
+ if ( op ) {
+ if ( op->state() != StFailed ) {
+ op->setState( TQNetworkProtocol::StDone );
+ op->setErrorCode( TQNetworkProtocol::NoError );
+ }
+ emit finished( op );
+ }
+ }
+
+}
+
+void TQHttp::clientStateChanged( int state )
+{
+ if ( url() ) {
+ switch ( (State)state ) {
+ case Connecting:
+ emit connectionStateChanged( ConHostFound, tr( "Host %1 found" ).arg( url()->host() ) );
+ break;
+ case Sending:
+ emit connectionStateChanged( ConConnected, tr( "Connected to host %1" ).arg( url()->host() ) );
+ break;
+ case Unconnected:
+ emit connectionStateChanged( ConClosed, tr( "Connection to %1 closed" ).arg( url()->host() ) );
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch ( (State)state ) {
+ case Connecting:
+ emit connectionStateChanged( ConHostFound, tr( "Host found" ) );
+ break;
+ case Sending:
+ emit connectionStateChanged( ConConnected, tr( "Connected to host" ) );
+ break;
+ case Unconnected:
+ emit connectionStateChanged( ConClosed, tr( "Connection closed" ) );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+#endif
diff --git a/src/network/qhttp.h b/src/network/qhttp.h
new file mode 100644
index 00000000..ed44c8e0
--- /dev/null
+++ b/src/network/qhttp.h
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Definition of TQHttp and related classes.
+**
+** Created : 970521
+**
+** Copyright (C) 1997-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQHTTP_H
+#define TQHTTP_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qnetworkprotocol.h"
+#include "qstringlist.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_HTTP
+#define TQM_TEMPLATE_EXTERN_HTTP
+#else
+#define TQM_EXPORT_HTTP Q_EXPORT
+#define TQM_TEMPLATE_EXTERN_HTTP Q_TEMPLATE_EXTERN
+#endif
+
+#ifndef QT_NO_NETWORKPROTOCOL_HTTP
+
+class TQSocket;
+class TQTimerEvent;
+class TQTextStream;
+class TQIODevice;
+
+class TQHttpPrivate;
+class TQHttpRequest;
+
+class TQM_EXPORT_HTTP TQHttpHeader
+{
+public:
+ TQHttpHeader();
+ TQHttpHeader( const TQHttpHeader& header );
+ TQHttpHeader( const TQString& str );
+ virtual ~TQHttpHeader();
+
+ TQHttpHeader& operator=( const TQHttpHeader& h );
+
+ TQString value( const TQString& key ) const;
+ void setValue( const TQString& key, const TQString& value );
+ void removeValue( const TQString& key );
+
+ TQStringList keys() const;
+ bool hasKey( const TQString& key ) const;
+
+ bool hasContentLength() const;
+ uint contentLength() const;
+ void setContentLength( int len );
+
+ bool hasContentType() const;
+ TQString contentType() const;
+ void setContentType( const TQString& type );
+
+ virtual TQString toString() const;
+ bool isValid() const;
+
+ virtual int majorVersion() const = 0;
+ virtual int minorVersion() const = 0;
+
+protected:
+ virtual bool parseLine( const TQString& line, int number );
+ bool parse( const TQString& str );
+ void setValid( bool );
+
+private:
+ TQMap<TQString,TQString> values;
+ bool valid;
+};
+
+class TQM_EXPORT_HTTP TQHttpResponseHeader : public TQHttpHeader
+{
+private:
+ TQHttpResponseHeader( int code, const TQString& text = TQString::null, int majorVer = 1, int minorVer = 1 );
+ TQHttpResponseHeader( const TQString& str );
+
+ void setStatusLine( int code, const TQString& text = TQString::null, int majorVer = 1, int minorVer = 1 );
+
+public:
+ TQHttpResponseHeader();
+ TQHttpResponseHeader( const TQHttpResponseHeader& header );
+
+ int statusCode() const;
+ TQString reasonPhrase() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ TQString toString() const;
+
+protected:
+ bool parseLine( const TQString& line, int number );
+
+private:
+ int statCode;
+ TQString reasonPhr;
+ int majVer;
+ int minVer;
+
+ friend class TQHttp;
+};
+
+class TQM_EXPORT_HTTP TQHttpRequestHeader : public TQHttpHeader
+{
+public:
+ TQHttpRequestHeader();
+ TQHttpRequestHeader( const TQString& method, const TQString& path, int majorVer = 1, int minorVer = 1 );
+ TQHttpRequestHeader( const TQHttpRequestHeader& header );
+ TQHttpRequestHeader( const TQString& str );
+
+ void setRequest( const TQString& method, const TQString& path, int majorVer = 1, int minorVer = 1 );
+
+ TQString method() const;
+ TQString path() const;
+
+ int majorVersion() const;
+ int minorVersion() const;
+
+ TQString toString() const;
+
+protected:
+ bool parseLine( const TQString& line, int number );
+
+private:
+ TQString m;
+ TQString p;
+ int majVer;
+ int minVer;
+};
+
+class TQM_EXPORT_HTTP TQHttp : public TQNetworkProtocol
+{
+ Q_OBJECT
+
+public:
+ TQHttp();
+ TQHttp( TQObject* parent, const char* name = 0 ); // ### TQt 4.0: make parent=0 and get rid of the TQHttp() constructor
+ TQHttp( const TQString &hostname, Q_UINT16 port=80, TQObject* parent=0, const char* name = 0 );
+ virtual ~TQHttp();
+
+ 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 TQString &hostname, Q_UINT16 port=80 );
+
+ int get( const TQString& path, TQIODevice* to=0 );
+ int post( const TQString& path, TQIODevice* data, TQIODevice* to=0 );
+ int post( const TQString& path, const TQByteArray& data, TQIODevice* to=0 );
+ int head( const TQString& path );
+ int request( const TQHttpRequestHeader &header, TQIODevice *device=0, TQIODevice *to=0 );
+ int request( const TQHttpRequestHeader &header, const TQByteArray &data, TQIODevice *to=0 );
+
+ int closeConnection();
+
+ Q_ULONG bytesAvailable() const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ TQByteArray readAll();
+
+ int currentId() const;
+ TQIODevice* currentSourceDevice() const;
+ TQIODevice* currentDestinationDevice() const;
+ TQHttpRequestHeader currentRequest() const;
+ bool hasPendingRequests() const;
+ void clearPendingRequests();
+
+ State state() const;
+
+ Error error() const;
+ TQString errorString() const;
+
+public slots:
+ void abort();
+
+signals:
+ void stateChanged( int );
+ void responseHeaderReceived( const TQHttpResponseHeader& resp );
+ void readyRead( const TQHttpResponseHeader& resp );
+ void dataSendProgress( int, int );
+ void dataReadProgress( int, int );
+
+ void requestStarted( int );
+ void requestFinished( int, bool );
+ void done( bool );
+
+protected:
+ void operationGet( TQNetworkOperation *op );
+ void operationPut( TQNetworkOperation *op );
+
+ void timerEvent( TQTimerEvent * );
+
+private slots:
+ void clientReply( const TQHttpResponseHeader &rep );
+ void clientDone( bool );
+ void clientStateChanged( int );
+
+ void startNextRequest();
+ void slotReadyRead();
+ void slotConnected();
+ void slotError( int );
+ void slotClosed();
+ void slotBytesWritten( int );
+
+private:
+ TQHttpPrivate *d;
+ void *unused; // ### TQt 4.0: remove this (in for binary compatibility)
+ int bytesRead;
+
+ int addRequest( TQHttpRequest * );
+ void sendRequest();
+ void finishedWithSuccess();
+ void finishedWithError( const TQString& detail, int errorCode );
+
+ void killIdleTimer();
+
+ void init();
+ void setState( int );
+ void close();
+
+ friend class TQHttpNormalRequest;
+ friend class TQHttpSetHostRequest;
+ friend class TQHttpCloseRequest;
+ friend class TQHttpPGHRequest;
+};
+
+#define Q_DEFINED_QHTTP
+#include "qwinexport.h"
+#endif
+#endif
diff --git a/src/network/qnetwork.cpp b/src/network/qnetwork.cpp
new file mode 100644
index 00000000..4043b1b6
--- /dev/null
+++ b/src/network/qnetwork.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Implementation of qInitNetworkProtocols function.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qnetwork.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qnetworkprotocol.h"
+
+// protocols
+#include "qftp.h"
+#include "qhttp.h"
+
+/*! \file qnetwork.h */
+/*!
+ \relates TQUrlOperator
+
+ This function registers the network protocols for FTP and HTTP.
+ You must call this function before you use TQUrlOperator for
+ these protocols.
+
+ This function is declared in \l qnetwork.h.
+*/
+void qInitNetworkProtocols()
+{
+#ifndef QT_NO_NETWORKPROTOCOL_FTP
+ TQNetworkProtocol::registerNetworkProtocol( "ftp", new TQNetworkProtocolFactory< TQFtp > );
+#endif
+#ifndef QT_NO_NETWORKPROTOCOL_HTTP
+ TQNetworkProtocol::registerNetworkProtocol( "http", new TQNetworkProtocolFactory< TQHttp > );
+#endif
+}
+
+#endif // QT_NO_NETWORK
diff --git a/src/network/qnetwork.h b/src/network/qnetwork.h
new file mode 100644
index 00000000..40213e11
--- /dev/null
+++ b/src/network/qnetwork.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Definition of qInitNetworkProtocols function.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQNETWORK_H
+#define TQNETWORK_H
+
+#ifndef QT_H
+#include "qglobal.h"
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_NETWORK
+#else
+#define TQM_EXPORT_NETWORK Q_EXPORT
+#endif
+
+#ifndef QT_NO_NETWORK
+
+TQM_EXPORT_NETWORK void qInitNetworkProtocols();
+
+#endif
+
+#endif
diff --git a/src/network/qserversocket.cpp b/src/network/qserversocket.cpp
new file mode 100644
index 00000000..7bb0cbe5
--- /dev/null
+++ b/src/network/qserversocket.cpp
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Implementation of TQServerSocket class.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qserversocket.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qsocketnotifier.h"
+
+class TQServerSocketPrivate {
+public:
+ TQServerSocketPrivate(): s(0), n(0) {}
+ ~TQServerSocketPrivate() { delete n; delete s; }
+ TQSocketDevice *s;
+ TQSocketNotifier *n;
+};
+
+
+/*!
+ \class TQServerSocket qserversocket.h
+ \brief The TQServerSocket class provides a TCP-based server.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ This class is a convenience class for accepting incoming TCP
+ connections. You can specify the port or have TQServerSocket pick
+ one, and listen on just one address or on all the machine's
+ addresses.
+
+ Using the API is very simple: subclass TQServerSocket, 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,
+ TQServerSocket cannot accept or reject connections conditionally.)
+
+ \sa TQSocket, TQSocketDevice, TQHostAddress, TQSocketNotifier
+*/
+
+
+/*!
+ Creates a server socket object, that will serve the given \a port
+ on all the addresses of this host. If \a port is 0, TQServerSocket
+ 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 TQObject
+ 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.
+*/
+
+TQServerSocket::TQServerSocket( Q_UINT16 port, int backlog,
+ TQObject *parent, const char *name )
+ : TQObject( parent, name )
+{
+ d = new TQServerSocketPrivate;
+ init( TQHostAddress(), 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 TQObject
+ 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.
+*/
+
+TQServerSocket::TQServerSocket( const TQHostAddress & address, Q_UINT16 port,
+ int backlog,
+ TQObject *parent, const char *name )
+ : TQObject( parent, name )
+{
+ d = new TQServerSocketPrivate;
+ init( address, port, backlog );
+}
+
+
+/*!
+ Construct an empty server socket.
+
+ This constructor, in combination with setSocket(), allows us to
+ use the TQServerSocket 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 TQObject
+ constructor.
+
+ \sa setSocket()
+*/
+
+TQServerSocket::TQServerSocket( TQObject *parent, const char *name )
+ : TQObject( parent, name )
+{
+ d = new TQServerSocketPrivate;
+}
+
+
+/*!
+ Returns TRUE if the construction succeeded; otherwise returns FALSE.
+*/
+bool TQServerSocket::ok() const
+{
+ return !!d->s;
+}
+
+/*
+ The common bit of the constructors.
+ */
+void TQServerSocket::init( const TQHostAddress & address, Q_UINT16 port, int backlog )
+{
+ d->s = new TQSocketDevice( TQSocketDevice::Stream, address.isIPv4Address()
+ ? TQSocketDevice::IPv4 : TQSocketDevice::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 TQSocketNotifier( d->s->socket(), TQSocketNotifier::Read,
+ this, "accepting new connections" );
+ connect( d->n, SIGNAL(activated(int)),
+ this, SLOT(incomingConnection(int)) );
+ } else {
+ qWarning( "TQServerSocket: 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
+ TQSocketDevice::accept()) to be severed.
+
+ Existing connections continue to exist; this only affects the
+ acceptance of new connections.
+*/
+TQServerSocket::~TQServerSocket()
+{
+ delete d;
+}
+
+
+/*!
+ \fn void TQServerSocket::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 TQServerSocket::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,
+ TQServerSocket will pick a non-zero port itself. ok() must be TRUE
+ before calling this function.
+
+ \sa address() TQSocketDevice::port()
+*/
+Q_UINT16 TQServerSocket::port() const
+{
+ if ( !d || !d->s )
+ return 0;
+ return d->s->port();
+}
+
+
+/*!
+ Returns the operating system socket.
+*/
+int TQServerSocket::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() TQSocketDevice::address()
+*/
+TQHostAddress TQServerSocket::address() const
+{
+ if ( !d || !d->s )
+ return TQHostAddress();
+
+ 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.
+*/
+TQSocketDevice *TQServerSocket::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 TQServerSocket class as a wrapper for
+ other socket types (e.g. Unix Domain Sockets).
+*/
+void TQServerSocket::setSocket( int socket )
+{
+ delete d;
+ d = new TQServerSocketPrivate;
+ d->s = new TQSocketDevice( socket, TQSocketDevice::Stream );
+ d->n = new TQSocketNotifier( d->s->socket(), TQSocketNotifier::Read,
+ this, "accepting new connections" );
+ connect( d->n, SIGNAL(activated(int)),
+ this, SLOT(incomingConnection(int)) );
+}
+
+#endif //QT_NO_NETWORK
diff --git a/src/network/qserversocket.h b/src/network/qserversocket.h
new file mode 100644
index 00000000..c536f61f
--- /dev/null
+++ b/src/network/qserversocket.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Definition of TQServerSocketClass.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSERVERSOCKET_H
+#define TQSERVERSOCKET_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qhostaddress.h"
+#include "qsocketdevice.h" // ### remove or keep for users' convenience?
+#endif // QT_H
+#ifndef QT_NO_NETWORK
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_NETWORK
+#else
+#define TQM_EXPORT_NETWORK Q_EXPORT
+#endif
+
+class TQServerSocketPrivate;
+
+
+class TQM_EXPORT_NETWORK TQServerSocket : public TQObject
+{
+ Q_OBJECT
+public:
+ TQServerSocket( Q_UINT16 port, int backlog = 1,
+ TQObject *parent=0, const char *name=0 );
+ TQServerSocket( const TQHostAddress & address, Q_UINT16 port, int backlog = 1,
+ TQObject *parent=0, const char *name=0 );
+ TQServerSocket( TQObject *parent=0, const char *name=0 );
+ virtual ~TQServerSocket();
+
+ bool ok() const;
+
+ Q_UINT16 port() const ;
+
+ int socket() const ;
+ virtual void setSocket( int socket );
+
+ TQHostAddress address() const ;
+
+ virtual void newConnection( int socket ) = 0;
+
+protected:
+ TQSocketDevice *socketDevice();
+
+private slots:
+ void incomingConnection( int socket );
+
+private:
+ TQServerSocketPrivate *d;
+ void init( const TQHostAddress & address, Q_UINT16 port, int backlog );
+};
+
+#endif // QT_NO_NETWORK
+#endif // TQSERVERSOCKET_H
diff --git a/src/network/qsocket.cpp b/src/network/qsocket.cpp
new file mode 100644
index 00000000..2e62f799
--- /dev/null
+++ b/src/network/qsocket.cpp
@@ -0,0 +1,1546 @@
+/****************************************************************************
+**
+** Implementation of TQSocket class.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsocket.h"
+#ifndef QT_NO_NETWORK
+#include "qptrlist.h"
+#include "qtimer.h"
+#include "qsocketdevice.h"
+#include "qdns.h"
+#include "private/qinternal_p.h"
+
+#include <string.h>
+#ifndef NO_ERRNO_H
+#include <errno.h>
+#endif
+
+//#define TQSOCKET_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 TQSocket available
+ as a separate class as a standard part of the TQt library, something along
+ the line of:
+
+ class TQByteBuffer : public TQIODevice { ... }
+
+ The same class could/would be used within TQSocket 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 = TQByteArray
+ buffer.close();
+
+ TQByteArray b;
+ b.resize( buffer.size() );
+ buffer.open( IO_ReadOnly );
+ buffer.readBlock( b.data(), b.size() );
+ buffer.close();
+
+ But would also be useable with TQDataStream (via TQIODevice) with:
+
+ buffer.open( IO_WriteOnly | IO_Append );
+ TQDataStream is( &buffer );
+ is << 100;
+ buffer.close();
+
+ buffer.open( IO_ReadOnly );
+ TQDataStream os( &buffer );
+ Q_UINT32 x;
+ os >> x;
+ buffer.close();
+
+ The real usefulness is with any situations where data (TQByteArray) arrives
+ incrementally (as in TQSocket and filter case above).
+
+ I tried using TQBuffer, but TQBuffer does not trim bytes from the front of
+ the buffer in cases like:
+
+ TQBuffer buf;
+ buf.open( IO_ReadOnly );
+ TQDataStream ds( &buf );
+ Q_INT32 x;
+ ds >> x;
+ buf.close();
+
+ In the above case, buf.size() will be identical before and after the
+ operation with TQDataStream. Based on the implementation of TQBuffer, it
+ does not appear well suited for this kind of operation.
+*/
+
+// Private class for TQSocket
+
+class TQSocketPrivate {
+public:
+ TQSocketPrivate();
+ ~TQSocketPrivate();
+ void closeSocket();
+ void close();
+ void connectionClosed();
+ void setSocketDevice( TQSocket *q, TQSocketDevice *device );
+
+ TQSocket::State state; // connection state
+ TQString host; // host name
+ Q_UINT16 port; // host port
+ TQSocketDevice *socket; // connection socket
+ TQSocketNotifier *rsn, *wsn; // socket notifiers
+ TQMembuf rba; // read buffer
+ Q_ULONG readBufferSize; // limit for the read buffer size
+ TQPtrList<TQByteArray> wba; // list of write bufs
+ TQHostAddress addr; // connection address
+ TQValueList<TQHostAddress> addresses; // alternatives looked up
+ TQIODevice::Offset wsize; // write total buf size
+ TQIODevice::Offset windex; // write index
+#ifndef QT_NO_DNS
+ TQDns *dns4;
+ TQDns *dns6;
+#endif
+ static TQPtrList<TQSocket> sn_read_alreadyCalled; // used to avoid unwanted recursion
+ TQValueList<TQHostAddress> l4;
+ TQValueList<TQHostAddress> l6;
+};
+
+TQPtrList<TQSocket> TQSocketPrivate::sn_read_alreadyCalled;
+
+TQSocketPrivate::TQSocketPrivate()
+ : state(TQSocket::Idle), host(TQString::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 );
+}
+
+TQSocketPrivate::~TQSocketPrivate()
+{
+ close();
+ delete socket;
+#ifndef QT_NO_DNS
+ delete dns4;
+ delete dns6;
+#endif
+}
+
+void TQSocketPrivate::closeSocket()
+{
+ // Order is important here - the socket notifiers must go away
+ // before the socket does, otherwise libc or the kernel will
+ // become unhappy.
+ delete rsn;
+ rsn = 0;
+ delete wsn;
+ wsn = 0;
+ if ( socket )
+ socket->close();
+}
+
+void TQSocketPrivate::close()
+{
+ closeSocket();
+ wsize = 0;
+ rba.clear(); wba.clear();
+ windex = 0;
+}
+
+void TQSocketPrivate::connectionClosed()
+{
+ // We keep the open state in case there's unread incoming data
+ state = TQSocket::Idle;
+ closeSocket();
+ wba.clear();
+ windex = wsize = 0;
+}
+
+void TQSocketPrivate::setSocketDevice( TQSocket *q, TQSocketDevice *device )
+{
+ delete socket;
+ delete rsn;
+ delete wsn;
+
+ if ( device ) {
+ socket = device;
+ } else {
+ socket = new TQSocketDevice( TQSocketDevice::Stream,
+ ( addr.isIPv4Address() ?
+ TQSocketDevice::IPv4 :
+ TQSocketDevice::IPv6 ), 0 );
+ socket->setBlocking( FALSE );
+ socket->setAddressReusable( TRUE );
+ }
+
+ rsn = new TQSocketNotifier( socket->socket(),
+ TQSocketNotifier::Read, q, "read" );
+ wsn = new TQSocketNotifier( socket->socket(),
+ TQSocketNotifier::Write, q, "write" );
+
+ TQObject::connect( rsn, SIGNAL(activated(int)), q, SLOT(sn_read()) );
+ rsn->setEnabled( FALSE );
+ TQObject::connect( wsn, SIGNAL(activated(int)), q, SLOT(sn_write()) );
+ wsn->setEnabled( FALSE );
+}
+
+/*!
+ \class TQSocket qsocket.h
+ \brief The TQSocket class provides a buffered TCP connection.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ It provides a totally non-blocking TQIODevice, and modifies and
+ extends the API of TQIODevice with socket-specific code.
+
+ Note that a TQApplication must have been constructed before this
+ class can be used.
+
+ The functions you're likely to call most are connectToHost(),
+ bytesAvailable(), canReadLine() and the ones it inherits from
+ TQIODevice.
+
+ 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 TQSocket
+ 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 TQSocketDevice used for this socket.
+
+ TQSocket inherits TQIODevice, and reimplements some functions. In
+ general, you can treat it as a TQIODevice for writing, and mostly
+ also for reading. The match isn't perfect, since the TQIODevice
+ API is designed for devices that are controlled by the same
+ machine, and an asynchronous peer-to-peer network connection isn't
+ tquite like that. For example, there is nothing that matches
+ TQIODevice::size() exactly. The documentation for open(), close(),
+ flush(), size(), at(), atEnd(), readBlock(), writeBlock(),
+ getch(), putch(), ungetch() and readLine() describes the
+ differences in detail.
+
+ \warning TQSocket is not suitable for use in threads. If you need
+ to uses sockets in threads use the lower-level TQSocketDevice class.
+
+ \warning Because TQt doesn't use the native socketstream
+ implementation on Mac OS X, TQSocket has an implicit transfer
+ latency of 100ms. You can achieve lower latency on Mac OS X by
+ using TQSocketDevice instead.
+
+ \sa TQSocketDevice, TQHostAddress, TQSocketNotifier
+*/
+
+
+/*!
+ Creates a TQSocket object in \c TQSocket::Idle state.
+
+ The \a parent and \a name arguments are passed on to the TQObject
+ constructor.
+
+ Note that a TQApplication must have been constructed before sockets
+ can be used.
+*/
+
+TQSocket::TQSocket( TQObject *parent, const char *name )
+ : TQObject( parent, name )
+{
+ d = new TQSocketPrivate;
+ setSocketDevice( 0 );
+ setFlags( IO_Direct );
+ resetStatus();
+}
+
+
+/*!
+ Destroys the socket. Closes the connection if necessary.
+
+ \sa close()
+*/
+
+TQSocket::~TQSocket()
+{
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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.
+*/
+
+TQSocketDevice *TQSocket::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 TQSocket; after setting the socket call connectToHost() to
+ make the connection.
+
+ This function is useful if you need to subclass TQSocketDevice and
+ want to use the TQSocket API, for example, to implement Unix domain
+ sockets.
+*/
+
+void TQSocket::setSocketDevice( TQSocketDevice *device )
+{
+ if ( state() != Idle )
+ close();
+ d->setSocketDevice( this, device );
+}
+
+/*!
+ \enum TQSocket::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.
+*/
+
+/*!
+ Returns the current state of the socket connection.
+
+ \sa TQSocket::State
+*/
+
+TQSocket::State TQSocket::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
+ TQSocket 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. TQSocket will do a normal DNS lookup if retquired. Note that
+ \a port is in native byte order, unlike some other libraries.
+
+ \sa state()
+*/
+
+void TQSocket::connectToHost( const TQString &host, Q_UINT16 port )
+{
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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 TQDns( host, TQDns::A );
+ d->dns6 = new TQDns( host, TQDns::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 TQSocket::tryConnecting()
+{
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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() == TQSocketDevice::NoError ) {
+ if ( d->wsn )
+ d->wsn->setEnabled( TRUE );
+ return; // not serious, try again later
+ }
+
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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 TQSocket::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 TQSocket::error( int )
+
+ This signal is emitted after an error occurred. The parameter is
+ the \l Error value.
+*/
+
+/*!
+ \fn void TQSocket::hostFound()
+
+ This signal is emitted after connectToHost() has been called and
+ the host lookup has succeeded.
+
+ \sa connected()
+*/
+
+
+/*!
+ \fn void TQSocket::connected()
+
+ This signal is emitted after connectToHost() has been called and a
+ connection has been successfully established.
+
+ \sa connectToHost(), connectionClosed()
+*/
+
+
+/*!
+ \fn void TQSocket::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 TQSocket::delayedCloseFinished()
+
+ This signal is emitted when a delayed close is finished.
+
+ If you call close() and there is buffered output data to be
+ written, TQSocket goes into the \c TQSocket::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 TQSocket::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 TQSocket::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 TQIODevice file mode \a m.
+ This function is called automatically when needed and you should
+ not call it yourself.
+
+ \sa close()
+*/
+
+bool TQSocket::open( int m )
+{
+ if ( isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "TQSocket::open: Already open" );
+#endif
+ return FALSE;
+ }
+ TQIODevice::setMode( m & IO_ReadWrite );
+ setState( IO_Open );
+ return TRUE;
+}
+
+
+/*!
+ Closes the socket.
+
+ The read buffer is cleared.
+
+ If the output buffer is empty, the state is set to \c
+ TQSocket::Idle and the connection is terminated immediately. If the
+ output buffer still contains data to be written, TQSocket goes into
+ the \c TQSocket::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 \c TQSocket::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 TQSocket::close()
+{
+ if ( !isOpen() || d->state == Idle ) // already closed
+ return;
+ if ( d->state == Closing )
+ return;
+ if ( !d->rsn || !d->wsn )
+ return;
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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;
+ }
+ setFlags( IO_Sequential );
+ resetStatus();
+ setState( 0 );
+ d->close();
+ d->state = Idle;
+}
+
+
+/*!
+ This function consumes \a nbytes bytes of data from the write
+ buffer.
+*/
+
+bool TQSocket::consumeWriteBuf( Q_ULONG nbytes )
+{
+ if ( nbytes <= 0 || nbytes > d->wsize )
+ return FALSE;
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): skipWriteBuf %d bytes", name(), (int)nbytes );
+#endif
+ d->wsize -= nbytes;
+ for ( ;; ) {
+ TQByteArray *a = d->wba.first();
+ if ( 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 TQIODevice::flush() function.
+*/
+
+void TQSocket::flush()
+{
+ if ( !d->socket )
+ return;
+ bool osBufferFull = FALSE;
+ int consumed = 0;
+ while ( !osBufferFull && d->state >= Connecting && d->wsize > 0 ) {
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): flush: Write data to the socket", name() );
+#endif
+ TQByteArray *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.
+ TQByteArray 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->writeBlock( out.data(), i );
+ if ( d->wsn )
+ d->wsn->setEnabled( FALSE ); // the TQSocketNotifier documentation says so
+ } else {
+ // Big block, write it immediately
+ i = a->size() - d->windex;
+ nwritten = d->socket->writeBlock( a->data() + d->windex, i );
+ if ( d->wsn )
+ d->wsn->setEnabled( FALSE ); // the TQSocketNotifier documentation says so
+ }
+ if ( nwritten > 0 ) {
+ if ( consumeWriteBuf( nwritten ) )
+ consumed += nwritten;
+ }
+ if ( nwritten < i )
+ osBufferFull = TRUE;
+ }
+ if ( consumed > 0 ) {
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): flush: Delayed close done. Terminating.",
+ name() );
+#endif
+ setFlags( IO_Sequential );
+ resetStatus();
+ setState( 0 );
+ d->close();
+ d->state = Idle;
+ emit delayedCloseFinished();
+ return;
+ }
+ if ( !d->socket->isOpen() ) {
+ d->connectionClosed();
+ emit connectionClosed();
+ return;
+ }
+ if ( d->wsn )
+ d->wsn->setEnabled( d->wsize > 0 ); // write if there's data
+}
+
+
+/*!
+ Returns the number of incoming bytes that can be read right now
+ (like bytesAvailable()).
+*/
+
+TQIODevice::Offset TQSocket::size() const
+{
+ return (Offset)bytesAvailable();
+}
+
+
+/*!
+ Returns the current read index. Since TQSocket is a sequential
+ device, the current read index is always zero.
+*/
+
+TQIODevice::Offset TQSocket::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 TQSocket::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 && TQSocketPrivate::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 TQSocket::atEnd() const
+{
+ if ( d->socket == 0 )
+ return TRUE;
+ TQSocket * that = (TQSocket *)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().
+
+ This function can trigger the readyRead() signal, if more data has
+ arrived on the socket.
+
+ \sa bytesToWrite()
+*/
+
+Q_ULONG TQSocket::bytesAvailable() const
+{
+ if ( d->socket == 0 )
+ return 0;
+ TQSocket * that = (TQSocket *)this;
+ if ( that->d->socket->bytesAvailable() ) // a little slow, perhaps...
+ (void)that->sn_read();
+ return that->d->rba.size();
+}
+
+
+/*!
+ 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 TQSocket::waitForMore( int msecs, bool *timeout ) const
+{
+ if ( d->socket == 0 )
+ return 0;
+ TQSocket * that = (TQSocket *)this;
+ if ( that->d->socket->waitForMore( msecs, timeout ) > 0 )
+ (void)that->sn_read( TRUE );
+ return that->d->rba.size();
+}
+
+/*! \overload
+*/
+
+Q_ULONG TQSocket::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()
+*/
+
+Q_ULONG TQSocket::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 TQSocket::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.
+*/
+
+Q_LONG TQSocket::readBlock( char *data, Q_ULONG maxlen )
+{
+ if ( data == 0 && maxlen != 0 ) {
+#if defined(QT_CHECK_NULL)
+ qWarning( "TQSocket::readBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "TQSocket::readBlock: Socket is not open" );
+#endif
+ return -1;
+ }
+ if ( maxlen >= d->rba.size() )
+ maxlen = d->rba.size();
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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 && TQSocketPrivate::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.
+*/
+
+Q_LONG TQSocket::writeBlock( const char *data, Q_ULONG len )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && len != 0 ) {
+ qWarning( "TQSocket::writeBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "TQSocket::writeBlock: Socket is not open" );
+ return -1;
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( d->state == Closing ) {
+ qWarning( "TQSocket::writeBlock: Cannot write, socket is closing" );
+ }
+#endif
+ if ( len == 0 || d->state == Closing || d->state == Idle )
+ return 0;
+ TQByteArray *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 TQByteArray( 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(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%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 TQSocket::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 && TQSocketPrivate::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 TQSocket::putch( int ch )
+{
+ char buf[2];
+ buf[0] = ch;
+ return writeBlock(buf, 1) == 1 ? ch : -1;
+}
+
+
+/*!
+ This implementation of the virtual function TQIODevice::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 TQSocket::ungetch( int ch )
+{
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "TQSocket::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:
+
+ \code
+ while( !socket->canReadLine() ) // WRONG
+ ;
+ \endcode
+
+ \sa readLine()
+*/
+
+bool TQSocket::canReadLine() const
+{
+ if ( ((TQSocket*)this)->d->rba.scanNewline( 0 ) )
+ return TRUE;
+ return ( bytesAvailable() > 0 &&
+ ((TQSocket*)this)->d->rba.scanNewline( 0 ) );
+}
+
+/*!
+ \reimp
+ \internal
+ So that it's not hidden by our other readLine().
+*/
+Q_LONG TQSocket::readLine( char *data, Q_ULONG maxlen )
+{
+ return TQIODevice::readLine(data,maxlen);
+}
+
+/*!
+ Returns a line of text including a terminating newline character
+ (\n). Returns "" if canReadLine() returns FALSE.
+
+ \sa canReadLine()
+*/
+
+TQString TQSocket::readLine()
+{
+ TQByteArray a(256);
+ bool nl = d->rba.scanNewline( &a );
+ TQString s;
+ if ( nl ) {
+ at( a.size() ); // skips the data read
+ s = TQString( a );
+ }
+ return s;
+}
+
+/*!
+ \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 TQSocket::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 TQSocketPrivate::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 && TQSocketPrivate::sn_read_alreadyCalled.findRef(this) != -1 )
+ return;
+ TQSocketPrivate::sn_read_alreadyCalled.append( this );
+
+ char buf[4096];
+ Q_LONG nbytes = d->socket->bytesAvailable();
+ Q_LONG nread;
+ TQByteArray *a = 0;
+
+ if ( state() == Connecting ) {
+ if ( nbytes > 0 ) {
+ tryConnection();
+ } else {
+ // nothing to do, nothing to care about
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ }
+ if ( state() == Idle ) {
+ TQSocketPrivate::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 TQSocket is used without an
+ // event loop.
+ nread = d->socket->readBlock( buf, maxToRead ? TQMIN((Q_LONG)sizeof(buf),maxToRead) : sizeof(buf) );
+ if ( nread == 0 ) { // really closed
+ if ( !d->socket->isOpen() ) {
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): sn_read: Connection closed", name() );
+#endif
+ d->connectionClosed();
+ emit connectionClosed();
+ }
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ } else {
+ if ( nread < 0 ) {
+ if ( d->socket->error() == TQSocketDevice::NoError ) {
+ // all is fine
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+#if defined(TQSOCKET_DEBUG)
+ qWarning( "TQSocket::sn_read (%s): Close error", name() );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( FALSE );
+ emit error( ErrSocketRead );
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ a = new TQByteArray( nread );
+ memcpy( a->data(), buf, nread );
+ }
+
+ } else { // data to be read
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): sn_read: %ld incoming bytes", name(), nbytes );
+#endif
+ if ( nbytes > (int)sizeof(buf) ) {
+ // big
+ a = new TQByteArray( nbytes );
+ nread = d->socket->readBlock( a->data(), maxToRead ? TQMIN(nbytes,maxToRead) : nbytes );
+ } else {
+ a = 0;
+ nread = d->socket->readBlock( buf, maxToRead ? TQMIN((Q_LONG)sizeof(buf),maxToRead) : sizeof(buf) );
+ if ( nread > 0 ) {
+ // ##### could setRawData
+ a = new TQByteArray( nread );
+ memcpy( a->data(), buf, nread );
+ }
+ }
+ if ( nread == 0 ) {
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): sn_read: Connection closed", name() );
+#endif
+ // ### we should rather ask the socket device if it is closed
+ d->connectionClosed();
+ emit connectionClosed();
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ delete a;
+ return;
+ } else if ( nread < 0 ) {
+ delete a;
+
+ if ( d->socket->error() == TQSocketDevice::NoError ) {
+ // all is fine
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+#if defined(QT_CHECK_RANGE)
+ qWarning( "TQSocket::sn_read: Read error" );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( FALSE );
+ emit error( ErrSocketRead );
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+ return;
+ }
+ if ( nread != (int)a->size() ) { // unexpected
+#if defined(CHECK_RANGE) && !defined(Q_OS_WIN32)
+ qWarning( "TQSocket::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 );
+ }
+
+ TQSocketPrivate::sn_read_alreadyCalled.removeRef( this );
+}
+
+
+/*!
+ \internal
+ Internal slot for handling socket write notifications.
+*/
+
+void TQSocket::sn_write()
+{
+ if ( d->state == Connecting ) // connection established?
+ tryConnection();
+ flush();
+}
+
+void TQSocket::emitErrorConnectionRefused()
+{
+ emit error( ErrConnectionRefused );
+}
+
+void TQSocket::tryConnection()
+{
+ if ( d->socket->connect( d->addr, d->port ) ) {
+ d->state = Connected;
+#if defined(TQSOCKET_DEBUG)
+ qDebug( "TQSocket (%s): sn_write: Got connection to %s",
+ name(), peerName().ascii() );
+#endif
+ if ( d->rsn )
+ d->rsn->setEnabled( TRUE );
+ emit connected();
+ } else {
+ d->state = Idle;
+ TQTimer::singleShot( 0, this, SLOT(emitErrorConnectionRefused()) );
+ return;
+ }
+}
+
+
+/*!
+ Returns the socket number, or -1 if there is no socket at the moment.
+*/
+
+int TQSocket::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 TQSocket class as a wrapper for other
+ socket types (e.g. Unix Domain Sockets).
+*/
+
+void TQSocket::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 TQSocket::setSocketIntern( int socket )
+{
+ if ( state() != Idle ) {
+ clearPendingData();
+ close();
+ }
+ Q_ULONG oldBufferSize = d ? d->readBufferSize : 0;
+ delete d;
+
+ d = new TQSocketPrivate;
+ if (oldBufferSize)
+ d->readBufferSize = oldBufferSize;
+ if ( socket >= 0 ) {
+ TQSocketDevice *sd = new TQSocketDevice( socket, TQSocketDevice::Stream );
+ sd->setBlocking( FALSE );
+ sd->setAddressReusable( TRUE );
+ d->setSocketDevice( this, sd );
+ }
+ d->state = Idle;
+
+ // Initialize the IO device flags
+ setFlags( IO_Direct );
+ resetStatus();
+ open( IO_ReadWrite );
+
+ // hm... this is not very nice.
+ d->host = TQString::null;
+ 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 TQSocket::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 TQt always uses native byte order, i.e. 67 is 67 in TQt;
+ there is no need to call htons().
+*/
+
+Q_UINT16 TQSocket::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.)
+*/
+
+TQHostAddress TQSocket::address() const
+{
+ if ( d->socket == 0 ) {
+ TQHostAddress tmp;
+ return tmp;
+ }
+ return d->socket->address();
+}
+
+
+/*!
+ Returns the address of the connected peer if the socket is in
+ Connected state; otherwise an empty TQHostAddress is returned.
+*/
+
+TQHostAddress TQSocket::peerAddress() const
+{
+ if ( d->socket == 0 ) {
+ TQHostAddress 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.
+*/
+
+TQString TQSocket::peerName() const
+{
+ return d->host;
+}
+
+/*!
+ Sets the size of the TQSocket's internal read buffer to \a bufSize.
+
+ Usually TQSocket 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 TQSocket 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 TQSocket::setReadBufferSize( Q_ULONG bufSize )
+{
+ d->readBufferSize = bufSize;
+}
+
+/*!
+ Returns the size of the read buffer.
+
+ \sa setReadBufferSize()
+*/
+
+Q_ULONG TQSocket::readBufferSize() const
+{
+ return d->readBufferSize;
+}
+
+#endif //QT_NO_NETWORK
diff --git a/src/network/qsocket.h b/src/network/qsocket.h
new file mode 100644
index 00000000..3472a63e
--- /dev/null
+++ b/src/network/qsocket.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Definition of TQSocket class.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSOCKET_H
+#define TQSOCKET_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qiodevice.h"
+#include "qhostaddress.h" // int->TQHostAddress conversion
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_NETWORK
+#else
+#define TQM_EXPORT_NETWORK Q_EXPORT
+#endif
+
+#ifndef QT_NO_NETWORK
+class TQSocketPrivate;
+class TQSocketDevice;
+
+
+class TQM_EXPORT_NETWORK TQSocket : public TQObject, public TQIODevice
+{
+ Q_OBJECT
+public:
+ enum Error {
+ ErrConnectionRefused,
+ ErrHostNotFound,
+ ErrSocketRead
+ };
+
+ TQSocket( TQObject *parent=0, const char *name=0 );
+ virtual ~TQSocket();
+
+ enum State { Idle, HostLookup, Connecting,
+ Connected, Closing,
+ Connection=Connected };
+ State state() const;
+
+ int socket() const;
+ virtual void setSocket( int );
+
+ TQSocketDevice *socketDevice();
+ virtual void setSocketDevice( TQSocketDevice * );
+
+#ifndef QT_NO_DNS
+ virtual void connectToHost( const TQString &host, Q_UINT16 port );
+#endif
+ TQString peerName() const;
+
+ // Implementation of TQIODevice abstract virtual functions
+ bool open( int mode );
+ void close();
+ void flush();
+ Offset size() const;
+ Offset at() const;
+ bool at( Offset );
+ bool atEnd() const;
+
+ Q_ULONG bytesAvailable() const; // ### TQIODevice::Offset instead?
+ Q_ULONG waitForMore( int msecs, bool *timeout ) const;
+ Q_ULONG waitForMore( int msecs ) const; // ### TQt 4.0: merge the two overloads
+ Q_ULONG bytesToWrite() const;
+ void clearPendingData();
+
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ Q_LONG writeBlock( const char *data, Q_ULONG len );
+ Q_LONG readLine( char *data, Q_ULONG maxlen );
+
+ int getch();
+ int putch( int );
+ int ungetch(int);
+
+ bool canReadLine() const;
+ virtual TQString readLine();
+
+ Q_UINT16 port() const;
+ Q_UINT16 peerPort() const;
+ TQHostAddress address() const;
+ TQHostAddress peerAddress() const;
+
+ void setReadBufferSize( Q_ULONG );
+ Q_ULONG readBufferSize() const;
+
+signals:
+ void hostFound();
+ void connected();
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void bytesWritten( int nbytes );
+ void error( int );
+
+protected slots:
+ virtual void sn_read( bool force=FALSE );
+ virtual void sn_write();
+
+private slots:
+ void tryConnecting();
+ void emitErrorConnectionRefused();
+
+private:
+ TQSocketPrivate *d;
+
+ bool consumeWriteBuf( Q_ULONG nbytes );
+ void tryConnection();
+ void setSocketIntern( int socket );
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ TQSocket( const TQSocket & );
+ TQSocket &operator=( const TQSocket & );
+#endif
+};
+
+#endif //QT_NO_NETWORK
+#endif // TQSOCKET_H
diff --git a/src/network/qsocketdevice.cpp b/src/network/qsocketdevice.cpp
new file mode 100644
index 00000000..10895a53
--- /dev/null
+++ b/src/network/qsocketdevice.cpp
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** Implementation of TQSocketDevice class.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qsocketdevice.h"
+#ifndef QT_NO_NETWORK
+
+#include "qwindowdefs.h"
+#include <string.h>
+
+
+//#define TQSOCKETDEVICE_DEBUG
+
+
+class TQSocketDevicePrivate
+{
+public:
+ TQSocketDevicePrivate( TQSocketDevice::Protocol p )
+ : protocol(p)
+ { }
+
+ TQSocketDevice::Protocol protocol;
+};
+
+
+/*!
+ \class TQSocketDevice qsocketdevice.h
+ \brief The TQSocketDevice class provides a platform-independent low-level socket API.
+\if defined(commercial)
+ It is part of the <a href="commercialeditions.html">TQt Enterprise Edition</a>.
+\endif
+
+ \ingroup io
+ \module network
+
+ 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
+ TQSocket class provides a much easier and high level alternative, but
+ certain things (like UDP) can't be done with TQSocket and if you need a
+ platform-independent API for those, TQSocketDevice is the right choice.
+
+ The essential purpose of the class is to provide a TQIODevice that
+ works on sockets, wrapped in a platform-independent API.
+
+ When calling connect() or bind(), TQSocketDevice detects the
+ protocol family (IPv4, IPv6) automatically. Passing the protocol
+ family to TQSocketDevice'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 TQSocket, TQSocketNotifier, TQHostAddress
+*/
+
+
+/*!
+ \enum TQSocketDevice::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 TQSocketDevice with an already existing socket; it
+ tries to determine the protocol family, but this can fail if the
+ protocol family is not known to TQSocketDevice.
+
+ \sa protocol() setSocket()
+*/
+
+/*!
+ \enum TQSocketDevice::Error
+
+ This enum type describes the error states of TQSocketDevice.
+
+ \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 TQSocketDevice.
+
+ \value Impossible An attempt was made to do something which makes
+ no sense. For example:
+ \code
+ ::close( sd->socket() );
+ sd->writeBlock( someData, 42 );
+ \endcode
+ The libc ::close() closes the socket, but TQSocketDevice is not aware
+ of this. So when you call writeBlock(), the impossible happens.
+
+ \value NoFiles The operating system will not let TQSocketDevice 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.
+*/
+
+/*!
+ \enum TQSocketDevice::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 TQSocketDevice object for the existing socket \a socket.
+
+ The \a type argument must match the actual socket type; use \c
+ TQSocketDevice::Stream for a reliable, connection-oriented TCP
+ socket, or \c TQSocketDevice::Datagram for an unreliable,
+ connectionless UDP socket.
+*/
+TQSocketDevice::TQSocketDevice( int socket, Type type )
+ : fd( socket ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new TQSocketDevicePrivate(Unknown))
+{
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice: Created TQSocketDevice %p (socket %x, type %d)",
+ this, socket, type );
+#endif
+ init();
+ setSocket( socket, type );
+}
+
+/*!
+ Creates a TQSocketDevice object for a stream or datagram socket.
+
+ The \a type argument must be either \c TQSocketDevice::Stream for a
+ reliable, connection-oriented TCP socket, or \c
+ TQSocketDevice::Datagram for an unreliable UDP socket.
+
+ The socket is created as an IPv4 socket.
+
+ \sa blocking() protocol()
+*/
+TQSocketDevice::TQSocketDevice( Type type )
+ : fd( -1 ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new TQSocketDevicePrivate(IPv4))
+{
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice: Created TQSocketDevice object %p, type %d",
+ this, type );
+#endif
+ init();
+ setSocket( createNewSocket(), type );
+}
+
+/*!
+ Creates a TQSocketDevice object for a stream or datagram socket.
+
+ The \a type argument must be either \c TQSocketDevice::Stream for a
+ reliable, connection-oriented TCP socket, or \c
+ TQSocketDevice::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()
+*/
+TQSocketDevice::TQSocketDevice( Type type, Protocol protocol, int )
+ : fd( -1 ), t( type ), p( 0 ), pp( 0 ), e( NoError ),
+ d(new TQSocketDevicePrivate(protocol))
+{
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice: Created TQSocketDevice object %p, type %d",
+ this, type );
+#endif
+ init();
+ setSocket( createNewSocket(), type );
+}
+
+/*!
+ Destroys the socket device and closes the socket if it is open.
+*/
+TQSocketDevice::~TQSocketDevice()
+{
+ close();
+ delete d;
+ d = 0;
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice: Destroyed TQSocketDevice %p", this );
+#endif
+}
+
+
+/*!
+ Returns TRUE if this is a valid socket; otherwise returns FALSE.
+
+ \sa socket()
+*/
+bool TQSocketDevice::isValid() const
+{
+ return fd != -1;
+}
+
+
+/*!
+ \fn Type TQSocketDevice::type() const
+
+ Returns the socket type which is either \c TQSocketDevice::Stream
+ or \c TQSocketDevice::Datagram.
+
+ \sa socket()
+*/
+TQSocketDevice::Type TQSocketDevice::type() const
+{
+ return t;
+}
+
+/*!
+ Returns the socket's protocol family, which is one of \c Unknown, \c IPv4,
+ or \c IPv6.
+
+ TQSocketDevice 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()
+*/
+TQSocketDevice::Protocol TQSocketDevice::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 TQSocketDevice::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
+ TQSocketDevice::Stream for a reliable, connection-oriented TCP
+ socket, or \c TQSocketDevice::Datagram for an unreliable,
+ connectionless UDP socket.
+
+ Any existing socket is closed.
+
+ \sa isValid(), close()
+*/
+void TQSocketDevice::setSocket( int socket, Type type )
+{
+ if ( fd != -1 ) // close any open socket
+ close();
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice::setSocket: socket %x, type %d", socket, type );
+#endif
+ t = type;
+ fd = socket;
+ d->protocol = Unknown;
+ e = NoError;
+ setFlags( IO_Sequential );
+ resetStatus();
+ open( IO_ReadWrite );
+ fetchConnectionParameters();
+}
+
+
+/*!
+ \reimp
+
+ Opens the socket using the specified TQIODevice file \a mode. This
+ function is called from the TQSocketDevice constructors and from
+ the setSocket() function. You should not call it yourself.
+
+ \sa close().
+*/
+bool TQSocketDevice::open( int mode )
+{
+ if ( isOpen() || !isValid() )
+ return FALSE;
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice::open: mode %x", mode );
+#endif
+ setMode( mode & IO_ReadWrite );
+ setState( IO_Open );
+ return TRUE;
+}
+
+
+/*!
+ \reimp
+
+ The current TQSocketDevice implementation does not buffer at all,
+ so this is a no-op.
+*/
+void TQSocketDevice::flush()
+{
+}
+
+
+/*!
+ \reimp
+
+ The size is meaningless for a socket, therefore this function returns 0.
+*/
+TQIODevice::Offset TQSocketDevice::size() const
+{
+ return 0;
+}
+
+
+/*!
+ \reimp
+
+ The read/write index is meaningless for a socket, therefore this
+ function returns 0.
+*/
+TQIODevice::Offset TQSocketDevice::at() const
+{
+ return 0;
+}
+
+
+/*!
+ \reimp
+
+ The read/write index is meaningless for a socket, therefore this
+ function does nothing and returns TRUE.
+*/
+bool TQSocketDevice::at( Offset )
+{
+ return TRUE;
+}
+
+
+/*!
+ \reimp
+
+ Returns TRUE if no data is currently available at the socket;
+ otherwise returns FALSE.
+*/
+bool TQSocketDevice::atEnd() const
+{
+ return bytesAvailable() <= 0;
+}
+
+
+/*!
+ \reimp
+
+ \warning getch() is implemented as a one-byte readBlock(), so it
+ may be very slow if you call it more than a few times.
+
+ \sa putch() readBlock()
+*/
+int TQSocketDevice::getch()
+{
+ char buf[2];
+ return readBlock(buf,1) == 1 ? buf[0] : -1;
+}
+
+
+/*!
+ \reimp
+
+ \warning putch() is implemented as a one-byte writeBlock(), so it
+ may be very slow if you call it more than a few times.
+
+ \sa getch()
+*/
+int TQSocketDevice::putch( int ch )
+{
+ char buf[2];
+ buf[0] = ch;
+ return writeBlock(buf, 1) == 1 ? ch : -1;
+}
+
+
+/*!
+ \reimp
+
+ This implementation of ungetch returns -1 (error). A socket is a
+ sequential device and does not allow any ungetch operation.
+*/
+int TQSocketDevice::ungetch( int )
+{
+ return -1;
+}
+
+
+/*!
+ 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 TQSocketDevice::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 TQSocketDevice::setAddressReusable( bool enable )
+{
+ setOption( ReuseAddress, enable );
+}
+
+
+/*!
+ Returns the size of the operating system receive buffer.
+
+ \sa setReceiveBufferSize()
+*/
+int TQSocketDevice::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 TQSocketDevice::setReceiveBufferSize( uint size )
+{
+ setOption( ReceiveBuffer, size );
+}
+
+
+/*!
+ Returns the size of the operating system send buffer.
+
+ \sa setSendBufferSize()
+*/
+int TQSocketDevice::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 TQSocketDevice::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 TQt always uses native byte order, i.e. 67 is 67 in TQt;
+ there is no need to call htons().
+*/
+Q_UINT16 TQSocketDevice::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.
+*/
+TQHostAddress TQSocketDevice::address() const
+{
+ return a;
+}
+
+
+/*!
+ Returns the first error seen.
+*/
+TQSocketDevice::Error TQSocketDevice::error() const
+{
+ return e;
+}
+
+
+/*!
+ Allows subclasses to set the error state to \a err.
+*/
+void TQSocketDevice::setError( Error err )
+{
+ e = err;
+}
+#endif //QT_NO_NETWORK
+
diff --git a/src/network/qsocketdevice.h b/src/network/qsocketdevice.h
new file mode 100644
index 00000000..06b7fd21
--- /dev/null
+++ b/src/network/qsocketdevice.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Definition of TQSocketDevice class.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#ifndef TQSOCKETDEVICE_H
+#define TQSOCKETDEVICE_H
+
+#ifndef QT_H
+#include "qiodevice.h"
+#include "qhostaddress.h" // int->TQHostAddress conversion
+#endif // QT_H
+
+#if !defined( QT_MODULE_NETWORK ) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_NETWORK )
+#define TQM_EXPORT_NETWORK
+#else
+#define TQM_EXPORT_NETWORK Q_EXPORT
+#endif
+
+#ifndef QT_NO_NETWORK
+class TQSocketDevicePrivate;
+
+
+class TQM_EXPORT_NETWORK TQSocketDevice: public TQIODevice
+{
+public:
+ enum Type { Stream, Datagram };
+ enum Protocol { IPv4, IPv6, Unknown };
+
+ TQSocketDevice( Type type = Stream );
+ TQSocketDevice( Type type, Protocol protocol, int dummy );
+ TQSocketDevice( int socket, Type type );
+ virtual ~TQSocketDevice();
+
+ bool isValid() const;
+ Type type() const;
+ Protocol protocol() const;
+
+ int socket() const;
+ virtual void setSocket( int socket, Type type );
+
+ bool open( int mode );
+ void close();
+ void flush();
+
+ // Implementation of TQIODevice 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 TQHostAddress &, Q_UINT16 );
+
+ virtual bool bind( const TQHostAddress &, Q_UINT16 );
+ virtual bool listen( int backlog );
+ virtual int accept();
+
+ Q_LONG bytesAvailable() const;
+ Q_LONG waitForMore( int msecs, bool *timeout=0 ) const;
+ Q_LONG readBlock( char *data, Q_ULONG maxlen );
+ Q_LONG writeBlock( const char *data, Q_ULONG len );
+ virtual Q_LONG writeBlock( const char *data, Q_ULONG len,
+ const TQHostAddress & host, Q_UINT16 port );
+
+ int getch();
+ int putch( int );
+ int ungetch(int);
+
+ Q_UINT16 port() const;
+ Q_UINT16 peerPort() const;
+ TQHostAddress address() const;
+ TQHostAddress peerAddress() const;
+
+ enum Error {
+ NoError,
+ AlreadyBound,
+ Inaccessible,
+ NoResources,
+ InternalError,
+ Bug = InternalError, // ### remove in 4.0?
+ Impossible,
+ NoFiles,
+ ConnectionRefused,
+ NetworkFailure,
+ UnknownError
+ };
+ Error error() const;
+
+protected:
+ void setError( Error err );
+
+private:
+ int fd;
+ Type t;
+ Q_UINT16 p;
+ TQHostAddress a;
+ Q_UINT16 pp;
+ TQHostAddress pa;
+ TQSocketDevice::Error e;
+ TQSocketDevicePrivate * d;
+
+ enum Option { Broadcast, ReceiveBuffer, ReuseAddress, SendBuffer };
+
+ int option( Option ) const;
+ virtual void setOption( Option, int );
+
+ void fetchConnectionParameters();
+#if defined(Q_OS_WIN32)
+ void fetchPeerConnectionParameters();
+#endif
+
+ static void init();
+ int createNewSocket();
+ Protocol getProtocol() const;
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ TQSocketDevice( const TQSocketDevice & );
+ TQSocketDevice &operator=( const TQSocketDevice & );
+#endif
+};
+
+#endif // QT_NO_NETWORK
+#endif // TQSOCKETDEVICE_H
diff --git a/src/network/qsocketdevice_unix.cpp b/src/network/qsocketdevice_unix.cpp
new file mode 100644
index 00000000..dab9208d
--- /dev/null
+++ b/src/network/qsocketdevice_unix.cpp
@@ -0,0 +1,1099 @@
+/****************************************************************************
+**
+** Implementation of TQSocketDevice class.
+**
+** Created : 970521
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the network module of the TQt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free TQt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing retquirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.TQPL
+** included in the packaging of this file. Licensees holding valid TQt
+** Commercial licenses may use this file in accordance with the TQt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#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
+
+// Solaris redefines bind -> __xnet_bind with _XOPEN_SOURCE_EXTENDED
+static inline int qt_socket_bind(int s, struct sockaddr *addr, QT_SOCKLEN_T addrlen)
+{ return ::bind(s, addr, addrlen); }
+#if defined(bind)
+# undef bind
+#endif
+
+// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED
+static inline int qt_socket_connect(int s, struct sockaddr *addr, QT_SOCKLEN_T addrlen)
+{ return ::connect(s, addr, addrlen); }
+#if defined(connect)
+# undef connect
+#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 "qsocketdevice.h"
+
+#ifndef QT_NO_NETWORK
+
+#include "qwindowdefs.h"
+
+#include <errno.h>
+#include <sys/types.h>
+
+
+static inline void qt_socket_getportaddr( struct sockaddr *sa,
+ Q_UINT16 *port, TQHostAddress *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) );
+ TQHostAddress a( tmp );
+ *addr = a;
+ *port = ntohs( sa6->sin6_port );
+ return;
+ }
+#endif
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ TQHostAddress a( ntohl( sa4->sin_addr.s_addr ) );
+ *port = ntohs( sa4->sin_port );
+ *addr = TQHostAddress( ntohl( sa4->sin_addr.s_addr ) );
+ return;
+}
+
+
+//#define TQSOCKETDEVICE_DEBUG
+
+// internal
+void TQSocketDevice::init()
+{
+}
+
+
+TQSocketDevice::Protocol TQSocketDevice::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;
+}
+
+/*!
+ Creates a new socket identifier. Returns -1 if there is a failure
+ to create the new identifier; error() explains why.
+
+ \sa setSocket()
+*/
+
+int TQSocketDevice::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 {
+ // ensure that the socket is closed on exec..() after being dup()'ed by
+ // fork() in TQProcess.
+ ::fcntl(s, F_SETFD, FD_CLOEXEC);
+ return s;
+ }
+ return -1;
+}
+
+/*!
+ \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-specfic errors that we haven't come
+ across, however.)
+
+ \sa open()
+*/
+void TQSocketDevice::close()
+{
+ if ( fd == -1 || !isOpen() ) // already closed
+ return;
+ setFlags( IO_Sequential );
+ resetStatus();
+ setState( 0 );
+ ::close( fd );
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice::close: Closed socket %x", fd );
+#endif
+ fd = -1;
+ fetchConnectionParameters();
+}
+
+
+/*!
+ 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()
+*/
+bool TQSocketDevice::blocking() const
+{
+ if ( !isValid() )
+ return TRUE;
+ int s = fcntl(fd, F_GETFL, 0);
+ return !(s >= 0 && ((s & O_NDELAY) != 0));
+}
+
+
+/*!
+ 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 TQSocketNotifier on Windows, the socket is
+ immediately made nonblocking.
+
+ \sa blocking(), isValid()
+*/
+void TQSocketDevice::setBlocking( bool enable )
+{
+#if defined(TQSOCKETDEVICE_DEBUG)
+ qDebug( "TQSocketDevice::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;
+ }
+}
+
+
+/*!
+ Returns the value of the socket option \a opt.
+*/
+int TQSocketDevice::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 ) {
+ TQSocketDevice *that = (TQSocketDevice*)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;
+}
+
+
+/*!
+ Sets the socket option \a opt to \a v.
+*/
+void TQSocketDevice::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;
+ }
+ }
+}
+
+
+/*!
+ 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.
+*/
+bool TQSocketDevice::connect( const TQHostAddress &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;
+}
+
+
+/*!
+ 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().
+*/
+bool TQSocketDevice::bind( const TQHostAddress &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;
+}
+
+
+/*!
+ 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 tquite 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()
+*/
+bool TQSocketDevice::listen( int backlog )
+{
+ if ( !isValid() )
+ return FALSE;
+ if ( qt_socket_listen( fd, backlog ) >= 0 )
+ return TRUE;
+ if ( !e )
+ e = Impossible;
+ return FALSE;
+}
+
+
+/*!
+ 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()
+*/
+int TQSocketDevice::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-tquickly case.
+ case EPERM:
+ // firewalling wouldn't let us accept. we treat it like
+ // the client-closed-tquickly 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);
+ // ensure that the socket is closed on exec..() after being dup()'ed by
+ // fork() in TQProcess.
+ ::fcntl(s, F_SETFD, FD_CLOEXEC);
+ return s;
+}
+
+
+/*!
+ 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(). TQSocket has
+ workarounds to deal with this problem.
+*/
+Q_LONG TQSocketDevice::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);
+}
+
+
+/*!
+ 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()
+*/
+Q_LONG TQSocketDevice::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();
+}
+
+
+/*!
+ 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.
+*/
+Q_LONG TQSocketDevice::readBlock( char *data, Q_ULONG maxlen )
+{
+#if defined(QT_CHECK_NULL)
+ if ( data == 0 && maxlen != 0 ) {
+ qWarning( "TQSocketDevice::readBlock: Null pointer error" );
+ }
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isValid() ) {
+ qWarning( "TQSocketDevice::readBlock: Invalid socket" );
+ return -1;
+ }
+ if ( !isOpen() ) {
+ qWarning( "TQSocketDevice::readBlock: Device is not open" );
+ return -1;
+ }
+ if ( !isReadable() ) {
+ qWarning( "TQSocketDevice::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;
+}
+
+
+/*!
+ 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 \c TQSocketDevice::Stream sockets.
+*/
+Q_LONG TQSocketDevice::writeBlock( const char *data, Q_ULONG len )
+{
+ if ( data == 0 && len != 0 ) {
+#if defined(QT_CHECK_NULL) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::writeBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::writeBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::writeBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::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;
+}
+
+
+/*!
+ \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 \c TQSocketDevice::Datagram sockets. You must
+ specify the \a host and \a port of the destination of the data.
+*/
+Q_LONG TQSocketDevice::writeBlock( const char * data, Q_ULONG len,
+ const TQHostAddress & host, Q_UINT16 port )
+{
+ if ( t != Datagram ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::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(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::sendBlock: Null pointer error" );
+#endif
+ return -1;
+ }
+ if ( !isValid() ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::sendBlock: Invalid socket" );
+#endif
+ return -1;
+ }
+ if ( !isOpen() ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::sendBlock: Device is not open" );
+#endif
+ return -1;
+ }
+ if ( !isWritable() ) {
+#if defined(QT_CHECK_STATE) || defined(TQSOCKETDEVICE_DEBUG)
+ qWarning( "TQSocketDevice::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;
+}
+
+
+/*!
+ Fetches information about both ends of the connection: whatever is
+ available.
+*/
+void TQSocketDevice::fetchConnectionParameters()
+{
+ if ( !isValid() ) {
+ p = 0;
+ a = TQHostAddress();
+ pp = 0;
+ pa = TQHostAddress();
+ 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 );
+}
+
+
+/*!
+ 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.
+*/
+Q_UINT16 TQSocketDevice::peerPort() const
+{
+ return pp;
+}
+
+
+/*!
+ 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.
+*/
+TQHostAddress TQSocketDevice::peerAddress() const
+{
+ return pa;
+}
+
+#endif //QT_NO_NETWORK
diff --git a/src/network/qt_network.pri b/src/network/qt_network.pri
new file mode 100644
index 00000000..86fcf24d
--- /dev/null
+++ b/src/network/qt_network.pri
@@ -0,0 +1,23 @@
+# Qt network module
+
+network {
+ HEADERS += $$NETWORK_H/qdns.h \
+ $$NETWORK_H/qftp.h \
+ $$NETWORK_H/qhttp.h \
+ $$NETWORK_H/qhostaddress.h \
+ $$NETWORK_H/qnetwork.h \
+ $$NETWORK_H/qserversocket.h \
+ $$NETWORK_H/qsocket.h \
+ $$NETWORK_H/qsocketdevice.h
+ NETWORK_SOURCES = $$NETWORK_CPP/qdns.cpp \
+ $$NETWORK_CPP/qftp.cpp \
+ $$NETWORK_CPP/qhttp.cpp \
+ $$NETWORK_CPP/qhostaddress.cpp \
+ $$NETWORK_CPP/qnetwork.cpp \
+ $$NETWORK_CPP/qserversocket.cpp \
+ $$NETWORK_CPP/qsocket.cpp \
+ $$NETWORK_CPP/qsocketdevice.cpp
+ unix:NETWORK_SOURCES += $$NETWORK_CPP/qsocketdevice_unix.cpp
+ win32:NETWORK_SOURCES += $$NETWORK_CPP/qsocketdevice_win.cpp
+ SOURCES += $$NETWORK_SOURCES
+}