src/qt3support/network/q3ftp.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt3Support module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qplatformdefs.h"
       
    43 #include "q3ftp.h"
       
    44 
       
    45 #ifndef QT_NO_NETWORKPROTOCOL_FTP
       
    46 
       
    47 #include "q3socket.h"
       
    48 #include "q3socketdevice.h"
       
    49 #include "qurlinfo.h"
       
    50 #include "q3urloperator.h"
       
    51 #include "qstringlist.h"
       
    52 #include "qregexp.h"
       
    53 #include "qtimer.h"
       
    54 #include "qfileinfo.h"
       
    55 #include "q3ptrdict.h"
       
    56 #include "q3cstring.h"
       
    57 #include "qcoreapplication.h"
       
    58 #include "qftp.h"
       
    59 
       
    60 #ifndef QT_NO_TEXTCODEC
       
    61 #include "qtextcodec.h"
       
    62 #endif
       
    63 
       
    64 QT_BEGIN_NAMESPACE
       
    65 
       
    66 //#define Q3FTPPI_DEBUG
       
    67 //#define Q3FTPDTP_DEBUG
       
    68 
       
    69 class Q3FtpPI;
       
    70 
       
    71 class Q3FtpDTP : public QObject
       
    72 {
       
    73     Q_OBJECT
       
    74 
       
    75 public:
       
    76     enum ConnectState {
       
    77 	CsHostFound,
       
    78 	CsConnected,
       
    79 	CsClosed,
       
    80 	CsHostNotFound,
       
    81 	CsConnectionRefused
       
    82     };
       
    83 
       
    84     Q3FtpDTP( Q3FtpPI *p, QObject *parent=0, const char *name=0 );
       
    85 
       
    86     void setData( QByteArray * );
       
    87     void setDevice( QIODevice * );
       
    88     void writeData();
       
    89 
       
    90     void setBytesTotal( int bytes )
       
    91     {
       
    92 	bytesTotal = bytes;
       
    93 	bytesDone = 0;
       
    94 	emit dataTransferProgress( bytesDone, bytesTotal );
       
    95     }
       
    96 
       
    97     bool hasError() const;
       
    98     QString errorMessage() const;
       
    99     void clearError();
       
   100 
       
   101     void connectToHost( const QString & host, Q_UINT16 port )
       
   102     { socket.connectToHost( host, port ); }
       
   103 
       
   104     Q3Socket::State socketState() const
       
   105     { return socket.state(); }
       
   106 
       
   107     Q_ULONG bytesAvailable() const
       
   108     { return socket.bytesAvailable(); }
       
   109 
       
   110     Q_LONG readBlock( char *data, Q_ULONG maxlen )
       
   111     {
       
   112 	Q_LONG read = socket.readBlock( data, maxlen );
       
   113 	bytesDone += read;
       
   114 	return read;
       
   115     }
       
   116 
       
   117     QByteArray readAll()
       
   118     {
       
   119 	QByteArray tmp = socket.readAll();
       
   120 	bytesDone += tmp.size();
       
   121 	return tmp;
       
   122     }
       
   123 
       
   124     void abortConnection();
       
   125 
       
   126     static bool parseDir( const QString &buffer, const QString &userName, QUrlInfo *info );
       
   127 
       
   128 signals:
       
   129     void listInfo( const QUrlInfo& );
       
   130     void readyRead();
       
   131     void dataTransferProgress( int, int );
       
   132 
       
   133     void connectState( int );
       
   134 
       
   135 private slots:
       
   136     void socketConnected();
       
   137     void socketReadyRead();
       
   138     void socketError( int );
       
   139     void socketConnectionClosed();
       
   140     void socketBytesWritten( int );
       
   141 
       
   142 private:
       
   143     void clearData()
       
   144     {
       
   145 	is_ba = false;
       
   146 	data.dev = 0;
       
   147     }
       
   148 
       
   149     Q3Socket socket;
       
   150     Q3FtpPI *pi;
       
   151     QString err;
       
   152     int bytesDone;
       
   153     int bytesTotal;
       
   154     bool callWriteData;
       
   155 
       
   156     // If is_ba is true, ba is used; ba is never 0.
       
   157     // Otherwise dev is used; dev can be 0 or not.
       
   158     union {
       
   159 	QByteArray *ba;
       
   160 	QIODevice *dev;
       
   161     } data;
       
   162     bool is_ba;
       
   163 };
       
   164 
       
   165 class Q3FtpPI : public QObject
       
   166 {
       
   167     Q_OBJECT
       
   168 
       
   169 public:
       
   170     Q3FtpPI( QObject *parent = 0 );
       
   171 
       
   172     void connectToHost( const QString &host, Q_UINT16 port );
       
   173 
       
   174     bool sendCommands( const QStringList &cmds );
       
   175     bool sendCommand( const QString &cmd )
       
   176     { return sendCommands( QStringList( cmd ) ); }
       
   177 
       
   178     void clearPendingCommands();
       
   179     void abort();
       
   180 
       
   181     QString currentCommand() const
       
   182     { return currentCmd; }
       
   183 
       
   184     bool rawCommand;
       
   185 
       
   186     Q3FtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it
       
   187 		 // makes the design simpler this way
       
   188 signals:
       
   189     void connectState( int );
       
   190     void finished( const QString& );
       
   191     void error( int, const QString& );
       
   192     void rawFtpReply( int, const QString& );
       
   193 
       
   194 private slots:
       
   195     void hostFound();
       
   196     void connected();
       
   197     void connectionClosed();
       
   198     void delayedCloseFinished();
       
   199     void readyRead();
       
   200     void error( int );
       
   201 
       
   202     void dtpConnectState( int );
       
   203 
       
   204 private:
       
   205     // the states are modelled after the generalized state diagram of RFC 959,
       
   206     // page 58
       
   207     enum State {
       
   208 	Begin,
       
   209 	Idle,
       
   210 	Waiting,
       
   211 	Success,
       
   212 	Failure
       
   213     };
       
   214 
       
   215     enum AbortState {
       
   216 	None,
       
   217 	AbortStarted,
       
   218 	WaitForAbortToFinish
       
   219     };
       
   220 
       
   221     bool processReply();
       
   222     bool startNextCmd();
       
   223 
       
   224     Q3Socket commandSocket;
       
   225     QString replyText;
       
   226     signed char replyCode[3];
       
   227     State state;
       
   228     AbortState abortState;
       
   229     QStringList pendingCommands;
       
   230     QString currentCmd;
       
   231 
       
   232     bool waitForDtpToConnect;
       
   233     bool waitForDtpToClose;
       
   234 };
       
   235 
       
   236 /**********************************************************************
       
   237  *
       
   238  * Q3FtpCommand implemenatation
       
   239  *
       
   240  *********************************************************************/
       
   241 class Q3FtpCommand
       
   242 {
       
   243 public:
       
   244     Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw );
       
   245     Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, const QByteArray &ba );
       
   246     Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, QIODevice *dev );
       
   247     ~Q3FtpCommand();
       
   248 
       
   249     int id;
       
   250     Q3Ftp::Command command;
       
   251     QStringList rawCmds;
       
   252 
       
   253     // If is_ba is true, ba is used; ba is never 0.
       
   254     // Otherwise dev is used; dev can be 0 or not.
       
   255     union {
       
   256 	QByteArray *ba;
       
   257 	QIODevice *dev;
       
   258     } data;
       
   259     bool is_ba;
       
   260 
       
   261     static int idCounter;
       
   262 };
       
   263 
       
   264 int Q3FtpCommand::idCounter = 0;
       
   265 
       
   266 Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw )
       
   267     : command(cmd), rawCmds(raw), is_ba(false)
       
   268 {
       
   269     id = ++idCounter;
       
   270     data.dev = 0;
       
   271 }
       
   272 
       
   273 Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, const QByteArray &ba )
       
   274     : command(cmd), rawCmds(raw), is_ba(true)
       
   275 {
       
   276     id = ++idCounter;
       
   277     data.ba = new QByteArray( ba );
       
   278 }
       
   279 
       
   280 Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw, QIODevice *dev )
       
   281     : command(cmd), rawCmds(raw), is_ba(false)
       
   282 {
       
   283     id = ++idCounter;
       
   284     data.dev = dev;
       
   285 }
       
   286 
       
   287 Q3FtpCommand::~Q3FtpCommand()
       
   288 {
       
   289     if ( is_ba )
       
   290 	delete data.ba;
       
   291 }
       
   292 
       
   293 /**********************************************************************
       
   294  *
       
   295  * Q3FtpDTP implemenatation
       
   296  *
       
   297  *********************************************************************/
       
   298 Q3FtpDTP::Q3FtpDTP( Q3FtpPI *p, QObject *parent, const char *name ) :
       
   299     QObject( parent, name ),
       
   300     socket( 0, "Q3FtpDTP_socket" ),
       
   301     pi( p ),
       
   302     callWriteData( false )
       
   303 {
       
   304     clearData();
       
   305 
       
   306     connect( &socket, SIGNAL(connected()),
       
   307 	     SLOT(socketConnected()) );
       
   308     connect( &socket, SIGNAL(readyRead()),
       
   309 	     SLOT(socketReadyRead()) );
       
   310     connect( &socket, SIGNAL(error(int)),
       
   311 	     SLOT(socketError(int)) );
       
   312     connect( &socket, SIGNAL(connectionClosed()),
       
   313 	     SLOT(socketConnectionClosed()) );
       
   314     connect( &socket, SIGNAL(bytesWritten(int)),
       
   315 	     SLOT(socketBytesWritten(int)) );
       
   316 }
       
   317 
       
   318 void Q3FtpDTP::setData( QByteArray *ba )
       
   319 {
       
   320     is_ba = true;
       
   321     data.ba = ba;
       
   322 }
       
   323 
       
   324 void Q3FtpDTP::setDevice( QIODevice *dev )
       
   325 {
       
   326     is_ba = false;
       
   327     data.dev = dev;
       
   328 }
       
   329 
       
   330 void Q3FtpDTP::writeData()
       
   331 {
       
   332     if ( is_ba ) {
       
   333 #if defined(Q3FTPDTP_DEBUG)
       
   334 	qDebug( "Q3FtpDTP::writeData: write %d bytes", data.ba->size() );
       
   335 #endif
       
   336 	if ( data.ba->size() == 0 )
       
   337 	    emit dataTransferProgress( 0, bytesTotal );
       
   338 	else
       
   339 	    socket.writeBlock( data.ba->data(), data.ba->size() );
       
   340 	socket.close();
       
   341 	clearData();
       
   342     } else if ( data.dev ) {
       
   343 	callWriteData = false;
       
   344 	const int blockSize = 16*1024;
       
   345 	char buf[blockSize];
       
   346 	while ( !data.dev->atEnd() && socket.bytesToWrite()==0 ) {
       
   347 	    Q_LONG read = data.dev->readBlock( buf, blockSize );
       
   348 #if defined(Q3FTPDTP_DEBUG)
       
   349 	    qDebug( "Q3FtpDTP::writeData: writeBlock() of size %d bytes", (int)read );
       
   350 #endif
       
   351 	    socket.writeBlock( buf, read );
       
   352 	    if ( !data.dev )
       
   353 		return; // this can happen when a command is aborted
       
   354 	}
       
   355 	if ( data.dev->atEnd() ) {
       
   356 	    if ( bytesDone==0 && socket.bytesToWrite()==0 )
       
   357 		emit dataTransferProgress( 0, bytesTotal );
       
   358 	    socket.close();
       
   359 	    clearData();
       
   360 	} else {
       
   361 	    callWriteData = true;
       
   362 	}
       
   363     }
       
   364 }
       
   365 
       
   366 inline bool Q3FtpDTP::hasError() const
       
   367 {
       
   368     return !err.isNull();
       
   369 }
       
   370 
       
   371 inline QString Q3FtpDTP::errorMessage() const
       
   372 {
       
   373     return err;
       
   374 }
       
   375 
       
   376 inline void Q3FtpDTP::clearError()
       
   377 {
       
   378     err.clear();
       
   379 }
       
   380 
       
   381 void Q3FtpDTP::abortConnection()
       
   382 {
       
   383 #if defined(Q3FTPDTP_DEBUG)
       
   384     qDebug( "Q3FtpDTP::abortConnection" );
       
   385 #endif
       
   386     callWriteData = false;
       
   387     clearData();
       
   388 
       
   389     socket.clearPendingData();
       
   390     socket.close();
       
   391 }
       
   392 
       
   393 bool Q3FtpDTP::parseDir( const QString &buffer, const QString &userName, QUrlInfo *info )
       
   394 {
       
   395     QStringList lst = QStringList::split( QLatin1String(" "), buffer );
       
   396 
       
   397     if ( lst.count() < 9 )
       
   398 	return false;
       
   399 
       
   400     QString tmp;
       
   401 
       
   402     // permissions
       
   403     tmp = lst[ 0 ];
       
   404 
       
   405     if ( tmp[ 0 ] == QChar( QLatin1Char('d') ) ) {
       
   406 	info->setDir( true );
       
   407 	info->setFile( false );
       
   408 	info->setSymLink( false );
       
   409     } else if ( tmp[ 0 ] == QChar( QLatin1Char('-') ) ) {
       
   410 	info->setDir( false );
       
   411 	info->setFile( true );
       
   412 	info->setSymLink( false );
       
   413     } else if ( tmp[ 0 ] == QChar( QLatin1Char('l') ) ) {
       
   414 	info->setDir( true ); // #### todo
       
   415 	info->setFile( false );
       
   416 	info->setSymLink( true );
       
   417     } else {
       
   418 	return false;
       
   419     }
       
   420 
       
   421     static int user = 0;
       
   422     static int group = 1;
       
   423     static int other = 2;
       
   424     static int readable = 0;
       
   425     static int writable = 1;
       
   426     static int executable = 2;
       
   427 
       
   428     bool perms[ 3 ][ 3 ];
       
   429     perms[0][0] = (tmp[ 1 ] == QLatin1Char('r'));
       
   430     perms[0][1] = (tmp[ 2 ] == QLatin1Char('w'));
       
   431     perms[0][2] = (tmp[ 3 ] == QLatin1Char('x'));
       
   432     perms[1][0] = (tmp[ 4 ] == QLatin1Char('r'));
       
   433     perms[1][1] = (tmp[ 5 ] == QLatin1Char('w'));
       
   434     perms[1][2] = (tmp[ 6 ] == QLatin1Char('x'));
       
   435     perms[2][0] = (tmp[ 7 ] == QLatin1Char('r'));
       
   436     perms[2][1] = (tmp[ 8 ] == QLatin1Char('w'));
       
   437     perms[2][2] = (tmp[ 9 ] == QLatin1Char('x'));
       
   438 
       
   439     // owner
       
   440     tmp = lst[ 2 ];
       
   441     info->setOwner( tmp );
       
   442 
       
   443     // group
       
   444     tmp = lst[ 3 ];
       
   445     info->setGroup( tmp );
       
   446 
       
   447     // ### not correct
       
   448     info->setWritable( ( userName == info->owner() && perms[ user ][ writable ] ) ||
       
   449 	perms[ other ][ writable ] );
       
   450     info->setReadable( ( userName == info->owner() && perms[ user ][ readable ] ) ||
       
   451 	perms[ other ][ readable ] );
       
   452 
       
   453     int p = 0;
       
   454     if ( perms[ user ][ readable ] )
       
   455 	p |= QUrlInfo::ReadOwner;
       
   456     if ( perms[ user ][ writable ] )
       
   457 	p |= QUrlInfo::WriteOwner;
       
   458     if ( perms[ user ][ executable ] )
       
   459 	p |= QUrlInfo::ExeOwner;
       
   460     if ( perms[ group ][ readable ] )
       
   461 	p |= QUrlInfo::ReadGroup;
       
   462     if ( perms[ group ][ writable ] )
       
   463 	p |= QUrlInfo::WriteGroup;
       
   464     if ( perms[ group ][ executable ] )
       
   465 	p |= QUrlInfo::ExeGroup;
       
   466     if ( perms[ other ][ readable ] )
       
   467 	p |= QUrlInfo::ReadOther;
       
   468     if ( perms[ other ][ writable ] )
       
   469 	p |= QUrlInfo::WriteOther;
       
   470     if ( perms[ other ][ executable ] )
       
   471 	p |= QUrlInfo::ExeOther;
       
   472     info->setPermissions( p );
       
   473 
       
   474     // size
       
   475     tmp = lst[ 4 ];
       
   476     info->setSize( tmp.toInt() );
       
   477 
       
   478     // date and time
       
   479     QTime time;
       
   480     QString dateStr;
       
   481     dateStr += QLatin1String("Sun ");
       
   482     lst[ 5 ][ 0 ] = lst[ 5 ][ 0 ].upper();
       
   483     dateStr += lst[ 5 ];
       
   484     dateStr += QLatin1Char(' ');
       
   485     dateStr += lst[ 6 ];
       
   486     dateStr += QLatin1Char(' ');
       
   487 
       
   488     if ( lst[ 7 ].contains( QLatin1Char(':') ) ) {
       
   489 	time = QTime( lst[ 7 ].left( 2 ).toInt(), lst[ 7 ].right( 2 ).toInt() );
       
   490 	dateStr += QString::number( QDate::currentDate().year() );
       
   491     } else {
       
   492 	dateStr += lst[ 7 ];
       
   493     }
       
   494 
       
   495     QDate date = QDate::fromString( dateStr );
       
   496     info->setLastModified( QDateTime( date, time ) );
       
   497 
       
   498     if ( lst[ 7 ].contains( QLatin1Char(':') ) ) {
       
   499 	const int futureTolerance = 600;
       
   500 	if( info->lastModified().secsTo( QDateTime::currentDateTime() ) < -futureTolerance ) {
       
   501 	    QDateTime dt = info->lastModified();
       
   502 	    QDate d = dt.date();
       
   503 	    d.setYMD(d.year()-1, d.month(), d.day());
       
   504 	    dt.setDate(d);
       
   505 	    info->setLastModified(dt);
       
   506 	}
       
   507     }
       
   508 
       
   509     // name
       
   510     if ( info->isSymLink() )
       
   511 	info->setName( lst[ 8 ].stripWhiteSpace() );
       
   512     else {
       
   513 	QString n;
       
   514 	for ( uint i = 8; i < (uint) lst.count(); ++i )
       
   515 	    n += lst[ i ] + QLatin1Char(' ');
       
   516 	n = n.stripWhiteSpace();
       
   517 	info->setName( n );
       
   518     }
       
   519     return true;
       
   520 }
       
   521 
       
   522 void Q3FtpDTP::socketConnected()
       
   523 {
       
   524 #if !defined (Q_WS_QWS)
       
   525     // Use a large send buffer to reduce the number
       
   526     // of writeBlocks when download and uploading files.
       
   527     // The actual size used here (128k) is default on most
       
   528     // Unixes.
       
   529     socket.socketDevice()->setSendBufferSize(128 * 1024);
       
   530     socket.socketDevice()->setReceiveBufferSize(128 * 1024);
       
   531 #endif
       
   532 
       
   533     bytesDone = 0;
       
   534 #if defined(Q3FTPDTP_DEBUG)
       
   535     qDebug( "Q3FtpDTP::connectState( CsConnected )" );
       
   536 #endif
       
   537     emit connectState( Q3FtpDTP::CsConnected );
       
   538 }
       
   539 
       
   540 void Q3FtpDTP::socketReadyRead()
       
   541 {
       
   542     if ( pi->currentCommand().isEmpty() ) {
       
   543 	socket.close();
       
   544 #if defined(Q3FTPDTP_DEBUG)
       
   545 	qDebug( "Q3FtpDTP::connectState( CsClosed )" );
       
   546 #endif
       
   547 	emit connectState( Q3FtpDTP::CsClosed );
       
   548 	return;
       
   549     }
       
   550 
       
   551     if ( pi->currentCommand().startsWith(QLatin1String("LIST")) ) {
       
   552 	while ( socket.canReadLine() ) {
       
   553 	    QUrlInfo i;
       
   554 	    QString line = QLatin1String(socket.readLine());
       
   555 #if defined(Q3FTPDTP_DEBUG)
       
   556 	    qDebug( "Q3FtpDTP read (list): '%s'", line.latin1() );
       
   557 #endif
       
   558 	    if ( parseDir( line, QLatin1String(""), &i ) ) {
       
   559 		emit listInfo( i );
       
   560 	    } else {
       
   561 		// some FTP servers don't return a 550 if the file or directory
       
   562 		// does not exist, but rather write a text to the data socket
       
   563 		// -- try to catch these cases
       
   564 		if ( line.endsWith( QLatin1String("No such file or directory\r\n") ) )
       
   565 		    err = line;
       
   566 	    }
       
   567 	}
       
   568     } else {
       
   569 	if ( !is_ba && data.dev ) {
       
   570 	    QByteArray ba( socket.bytesAvailable() );
       
   571 	    Q_LONG bytesRead = socket.readBlock( ba.data(), ba.size() );
       
   572 	    if ( bytesRead < 0 ) {
       
   573 		// ### error handling
       
   574 		return;
       
   575 	    }
       
   576 	    ba.resize( bytesRead );
       
   577 	    bytesDone += bytesRead;
       
   578 #if defined(Q3FTPDTP_DEBUG)
       
   579 	    qDebug( "Q3FtpDTP read: %d bytes (total %d bytes)", (int)bytesRead, bytesDone );
       
   580 #endif
       
   581 	    emit dataTransferProgress( bytesDone, bytesTotal );
       
   582             if (data.dev)       // make sure it wasn't deleted in the slot
       
   583                 data.dev->writeBlock( ba );
       
   584 	} else {
       
   585 #if defined(Q3FTPDTP_DEBUG)
       
   586 	    qDebug( "Q3FtpDTP readyRead: %d bytes available (total %d bytes read)", (int)bytesAvailable(), bytesDone );
       
   587 #endif
       
   588 	    emit dataTransferProgress( bytesDone+socket.bytesAvailable(), bytesTotal );
       
   589 	    emit readyRead();
       
   590 	}
       
   591     }
       
   592 }
       
   593 
       
   594 void Q3FtpDTP::socketError( int e )
       
   595 {
       
   596     if ( e == Q3Socket::ErrHostNotFound ) {
       
   597 #if defined(Q3FTPDTP_DEBUG)
       
   598 	qDebug( "Q3FtpDTP::connectState( CsHostNotFound )" );
       
   599 #endif
       
   600 	emit connectState( Q3FtpDTP::CsHostNotFound );
       
   601     } else if ( e == Q3Socket::ErrConnectionRefused ) {
       
   602 #if defined(Q3FTPDTP_DEBUG)
       
   603 	qDebug( "Q3FtpDTP::connectState( CsConnectionRefused )" );
       
   604 #endif
       
   605 	emit connectState( Q3FtpDTP::CsConnectionRefused );
       
   606     }
       
   607 }
       
   608 
       
   609 void Q3FtpDTP::socketConnectionClosed()
       
   610 {
       
   611     if ( !is_ba && data.dev ) {
       
   612 	clearData();
       
   613     }
       
   614 #if defined(Q3FTPDTP_DEBUG)
       
   615     qDebug( "Q3FtpDTP::connectState( CsClosed )" );
       
   616 #endif
       
   617     emit connectState( Q3FtpDTP::CsClosed );
       
   618 }
       
   619 
       
   620 void Q3FtpDTP::socketBytesWritten( int bytes )
       
   621 {
       
   622     bytesDone += bytes;
       
   623 #if defined(Q3FTPDTP_DEBUG)
       
   624     qDebug( "Q3FtpDTP::bytesWritten( %d )", bytesDone );
       
   625 #endif
       
   626     emit dataTransferProgress( bytesDone, bytesTotal );
       
   627     if ( callWriteData )
       
   628 	writeData();
       
   629 }
       
   630 
       
   631 /**********************************************************************
       
   632  *
       
   633  * Q3FtpPI implemenatation
       
   634  *
       
   635  *********************************************************************/
       
   636 Q3FtpPI::Q3FtpPI( QObject *parent ) :
       
   637     QObject( parent ),
       
   638     rawCommand(false),
       
   639     dtp( this ),
       
   640     commandSocket( 0, "Q3FtpPI_socket" ),
       
   641     state( Begin ), abortState( None ),
       
   642     currentCmd( QString() ),
       
   643     waitForDtpToConnect( false ),
       
   644     waitForDtpToClose( false )
       
   645 {
       
   646     connect( &commandSocket, SIGNAL(hostFound()),
       
   647 	    SLOT(hostFound()) );
       
   648     connect( &commandSocket, SIGNAL(connected()),
       
   649 	    SLOT(connected()) );
       
   650     connect( &commandSocket, SIGNAL(connectionClosed()),
       
   651 	    SLOT(connectionClosed()) );
       
   652     connect( &commandSocket, SIGNAL(delayedCloseFinished()),
       
   653 	    SLOT(delayedCloseFinished()) );
       
   654     connect( &commandSocket, SIGNAL(readyRead()),
       
   655 	    SLOT(readyRead()) );
       
   656     connect( &commandSocket, SIGNAL(error(int)),
       
   657 	    SLOT(error(int)) );
       
   658 
       
   659     connect( &dtp, SIGNAL(connectState(int)),
       
   660 	     SLOT(dtpConnectState(int)) );
       
   661 }
       
   662 
       
   663 void Q3FtpPI::connectToHost( const QString &host, Q_UINT16 port )
       
   664 {
       
   665     emit connectState( Q3Ftp::HostLookup );
       
   666     commandSocket.connectToHost( host, port );
       
   667 }
       
   668 
       
   669 /*
       
   670   Sends the sequence of commands \a cmds to the FTP server. When the commands
       
   671   are all done the finished() signal is emitted. When an error occurs, the
       
   672   error() signal is emitted.
       
   673 
       
   674   If there are pending commands in the queue this functions returns false and
       
   675   the \a cmds are not added to the queue; otherwise it returns true.
       
   676 */
       
   677 bool Q3FtpPI::sendCommands( const QStringList &cmds )
       
   678 {
       
   679     if ( !pendingCommands.isEmpty() )
       
   680 	return false;
       
   681 
       
   682     if ( commandSocket.state()!=Q3Socket::Connected || state!=Idle ) {
       
   683 	emit error( Q3Ftp::NotConnected, QFtp::tr( "Not connected" ) );
       
   684 	return true; // there are no pending commands
       
   685     }
       
   686 
       
   687     pendingCommands = cmds;
       
   688     startNextCmd();
       
   689     return true;
       
   690 }
       
   691 
       
   692 void Q3FtpPI::clearPendingCommands()
       
   693 {
       
   694     pendingCommands.clear();
       
   695     dtp.abortConnection();
       
   696     currentCmd.clear();
       
   697     state = Idle;
       
   698 }
       
   699 
       
   700 void Q3FtpPI::abort()
       
   701 {
       
   702     pendingCommands.clear();
       
   703 
       
   704     if ( abortState != None )
       
   705 	// ABOR already sent
       
   706 	return;
       
   707 
       
   708     abortState = AbortStarted;
       
   709 #if defined(Q3FTPPI_DEBUG)
       
   710     qDebug( "Q3FtpPI send: ABOR" );
       
   711 #endif
       
   712     commandSocket.writeBlock( "ABOR\r\n", 6 );
       
   713 
       
   714     if ( currentCmd.startsWith(QLatin1String("STOR ")) )
       
   715 	dtp.abortConnection();
       
   716 }
       
   717 
       
   718 void Q3FtpPI::hostFound()
       
   719 {
       
   720     emit connectState( Q3Ftp::Connecting );
       
   721 }
       
   722 
       
   723 void Q3FtpPI::connected()
       
   724 {
       
   725     state = Begin;
       
   726 #if defined(Q3FTPPI_DEBUG)
       
   727 //    qDebug( "Q3FtpPI state: %d [connected()]", state );
       
   728 #endif
       
   729     emit connectState( Q3Ftp::Connected );
       
   730 }
       
   731 
       
   732 void Q3FtpPI::connectionClosed()
       
   733 {
       
   734     commandSocket.close();
       
   735     emit connectState( Q3Ftp::Unconnected );
       
   736 }
       
   737 
       
   738 void Q3FtpPI::delayedCloseFinished()
       
   739 {
       
   740     emit connectState( Q3Ftp::Unconnected );
       
   741 }
       
   742 
       
   743 void Q3FtpPI::error( int e )
       
   744 {
       
   745     if ( e == Q3Socket::ErrHostNotFound ) {
       
   746 	emit connectState( Q3Ftp::Unconnected );
       
   747 	emit error( Q3Ftp::HostNotFound,
       
   748 		    QFtp::tr( "Host %1 not found" ).arg( commandSocket.peerName() ) );
       
   749     } else if ( e == Q3Socket::ErrConnectionRefused ) {
       
   750 	emit connectState( Q3Ftp::Unconnected );
       
   751 	emit error( Q3Ftp::ConnectionRefused,
       
   752 		    QFtp::tr( "Connection refused to host %1" ).arg( commandSocket.peerName() ) );
       
   753     }
       
   754 }
       
   755 
       
   756 void Q3FtpPI::readyRead()
       
   757 {
       
   758     if ( waitForDtpToClose )
       
   759 	return;
       
   760 
       
   761     while ( commandSocket.canReadLine() ) {
       
   762 	// read line with respect to line continuation
       
   763 	QString line = QLatin1String(commandSocket.readLine());
       
   764 	if ( replyText.isEmpty() ) {
       
   765 	    if ( line.length() < 3 ) {
       
   766 		// ### protocol error
       
   767 		return;
       
   768 	    }
       
   769 	    const int lowerLimit[3] = {1,0,0};
       
   770 	    const int upperLimit[3] = {5,5,9};
       
   771 	    for ( int i=0; i<3; i++ ) {
       
   772 		replyCode[i] = line[i].digitValue();
       
   773 		if ( replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i] ) {
       
   774 		    // ### protocol error
       
   775 		    return;
       
   776 		}
       
   777 	    }
       
   778 	}
       
   779 	QString endOfMultiLine;
       
   780 	endOfMultiLine[0] = '0' + replyCode[0];
       
   781 	endOfMultiLine[1] = '0' + replyCode[1];
       
   782 	endOfMultiLine[2] = '0' + replyCode[2];
       
   783 	endOfMultiLine[3] = ' ';
       
   784 	QString lineCont( endOfMultiLine );
       
   785 	lineCont[3] = '-';
       
   786 	QString lineLeft4 = line.left(4);
       
   787 
       
   788 	while ( lineLeft4 != endOfMultiLine ) {
       
   789 	    if ( lineLeft4 == lineCont )
       
   790 		replyText += line.mid( 4 ); // strip 'xyz-'
       
   791 	    else
       
   792 		replyText += line;
       
   793 	    if ( !commandSocket.canReadLine() )
       
   794 		return;
       
   795 	    line = QLatin1String(commandSocket.readLine());
       
   796 	    lineLeft4 = line.left(4);
       
   797 	}
       
   798 	replyText += line.mid( 4 ); // strip reply code 'xyz '
       
   799 	if ( replyText.endsWith(QLatin1String("\r\n")) )
       
   800 	    replyText.truncate( replyText.length()-2 );
       
   801 
       
   802 	if ( processReply() )
       
   803 	    replyText = QLatin1String("");
       
   804     }
       
   805 }
       
   806 
       
   807 /*
       
   808   Process a reply from the FTP server.
       
   809 
       
   810   Returns true if the reply was processed or false if the reply has to be
       
   811   processed at a later point.
       
   812 */
       
   813 bool Q3FtpPI::processReply()
       
   814 {
       
   815 #if defined(Q3FTPPI_DEBUG)
       
   816 //    qDebug( "Q3FtpPI state: %d [processReply() begin]", state );
       
   817     if ( replyText.length() < 400 )
       
   818 	qDebug( "Q3FtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.latin1() );
       
   819     else
       
   820 	qDebug( "Q3FtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2] );
       
   821 #endif
       
   822 
       
   823     // process 226 replies ("Closing Data Connection") only when the data
       
   824     // connection is really closed to avoid short reads of the DTP
       
   825     if ( 100*replyCode[0]+10*replyCode[1]+replyCode[2] == 226 ) {
       
   826 	if ( dtp.socketState() != Q3Socket::Idle ) {
       
   827 	    waitForDtpToClose = true;
       
   828 	    return false;
       
   829 	}
       
   830     }
       
   831 
       
   832     switch ( abortState ) {
       
   833 	case AbortStarted:
       
   834 	    abortState = WaitForAbortToFinish;
       
   835 	    break;
       
   836 	case WaitForAbortToFinish:
       
   837 	    abortState = None;
       
   838 	    return true;
       
   839 	default:
       
   840 	    break;
       
   841     }
       
   842 
       
   843     // get new state
       
   844     static const State table[5] = {
       
   845 	/* 1yz   2yz      3yz   4yz      5yz */
       
   846 	Waiting, Success, Idle, Failure, Failure
       
   847     };
       
   848     switch ( state ) {
       
   849 	case Begin:
       
   850 	    if ( replyCode[0] == 1 ) {
       
   851 		return true;
       
   852 	    } else if ( replyCode[0] == 2 ) {
       
   853 		state = Idle;
       
   854 		emit finished( QFtp::tr( "Connected to host %1" ).arg( commandSocket.peerName() ) );
       
   855 		break;
       
   856 	    }
       
   857 	    // ### error handling
       
   858 	    return true;
       
   859 	case Waiting:
       
   860 	    if ( replyCode[0]<0 || replyCode[0]>5 )
       
   861 		state = Failure;
       
   862 	    else
       
   863 #if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
       
   864             {
       
   865                 // work around a crash on 64 bit gcc IRIX
       
   866                 State *t = (State *) table;
       
   867                 state = t[replyCode[0] - 1];
       
   868             }
       
   869 #else
       
   870             state = table[replyCode[0] - 1];
       
   871 #endif
       
   872 	    break;
       
   873 	default:
       
   874 	    // ### spontaneous message
       
   875 	    return true;
       
   876     }
       
   877 #if defined(Q3FTPPI_DEBUG)
       
   878 //    qDebug( "Q3FtpPI state: %d [processReply() intermediate]", state );
       
   879 #endif
       
   880 
       
   881     // special actions on certain replies
       
   882     int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
       
   883     emit rawFtpReply( replyCodeInt, replyText );
       
   884     if ( rawCommand ) {
       
   885 	rawCommand = false;
       
   886     } else if ( replyCodeInt == 227 ) {
       
   887 	// 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
       
   888         // rfc959 does not define this response precisely, and gives
       
   889         // both examples where the parenthesis are used, and where
       
   890         // they are missing. We need to scan for the address and host
       
   891         // info.
       
   892 	QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
       
   893 	if (addrPortPattern.search(replyText) == -1) {
       
   894 #if defined(Q3FTPPI_DEBUG)
       
   895 	    qDebug( "Q3Ftp: bad 227 response -- address and port information missing" );
       
   896 #endif
       
   897 	    // ### error handling
       
   898 	} else {
       
   899 	    QStringList lst = addrPortPattern.capturedTexts();
       
   900 	    QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];
       
   901 	    Q_UINT16 port = ( lst[5].toUInt() << 8 ) + lst[6].toUInt();
       
   902 	    waitForDtpToConnect = true;
       
   903 	    dtp.connectToHost( host, port );
       
   904 	}
       
   905     } else if ( replyCodeInt == 230 ) {
       
   906 	if ( currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
       
   907 		pendingCommands.first().startsWith(QLatin1String("PASS ")) ) {
       
   908 	    // no need to send the PASS -- we are already logged in
       
   909 	    pendingCommands.pop_front();
       
   910 	}
       
   911 	// 230 User logged in, proceed.
       
   912 	emit connectState( Q3Ftp::LoggedIn );
       
   913     } else if ( replyCodeInt == 213 ) {
       
   914 	// 213 File status.
       
   915 	if ( currentCmd.startsWith(QLatin1String("SIZE ")) )
       
   916 	    dtp.setBytesTotal( replyText.simplifyWhiteSpace().toInt() );
       
   917     } else if ( replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR ")) ) {
       
   918 	dtp.writeData();
       
   919     }
       
   920 
       
   921     // react on new state
       
   922     switch ( state ) {
       
   923 	case Begin:
       
   924 	    // ### should never happen
       
   925 	    break;
       
   926 	case Success:
       
   927 	    // ### success handling
       
   928 	    state = Idle;
       
   929 	    // no break!
       
   930 	case Idle:
       
   931 	    if ( dtp.hasError() ) {
       
   932 		emit error( Q3Ftp::UnknownError, dtp.errorMessage() );
       
   933 		dtp.clearError();
       
   934 	    }
       
   935 	    startNextCmd();
       
   936 	    break;
       
   937 	case Waiting:
       
   938 	    // ### do nothing
       
   939 	    break;
       
   940 	case Failure:
       
   941 	    emit error( Q3Ftp::UnknownError, replyText );
       
   942 	    state = Idle;
       
   943 	    startNextCmd();
       
   944 	    break;
       
   945     }
       
   946 #if defined(Q3FTPPI_DEBUG)
       
   947 //    qDebug( "Q3FtpPI state: %d [processReply() end]", state );
       
   948 #endif
       
   949     return true;
       
   950 }
       
   951 
       
   952 #ifndef QT_NO_TEXTCODEC
       
   953 Q_COMPAT_EXPORT QTextCodec *qt_ftp_filename_codec = 0;
       
   954 #endif
       
   955 
       
   956 /*
       
   957   Starts next pending command. Returns false if there are no pending commands,
       
   958   otherwise it returns true.
       
   959 */
       
   960 bool Q3FtpPI::startNextCmd()
       
   961 {
       
   962     if ( waitForDtpToConnect )
       
   963 	// don't process any new commands until we are connected
       
   964 	return true;
       
   965 
       
   966 #if defined(Q3FTPPI_DEBUG)
       
   967     if ( state != Idle )
       
   968 	qDebug( "Q3FtpPI startNextCmd: Internal error! Q3FtpPI called in non-Idle state %d", state );
       
   969 #endif
       
   970     if ( pendingCommands.isEmpty() ) {
       
   971 	currentCmd.clear();
       
   972 	emit finished( replyText );
       
   973 	return false;
       
   974     }
       
   975     currentCmd = pendingCommands.first();
       
   976     pendingCommands.pop_front();
       
   977 #if defined(Q3FTPPI_DEBUG)
       
   978     qDebug( "Q3FtpPI send: %s", currentCmd.left( currentCmd.length()-2 ).latin1() );
       
   979 #endif
       
   980     state = Waiting;
       
   981 #ifndef QT_NO_TEXTCODEC
       
   982     if ( qt_ftp_filename_codec ) {
       
   983 	int len;
       
   984 	Q3CString enc = qt_ftp_filename_codec->fromUnicode(currentCmd,len);
       
   985 	commandSocket.writeBlock( enc.data(), len );
       
   986     } else
       
   987 #endif
       
   988     {
       
   989 	commandSocket.writeBlock( currentCmd.latin1(), currentCmd.length() );
       
   990     }
       
   991     return true;
       
   992 }
       
   993 
       
   994 void Q3FtpPI::dtpConnectState( int s )
       
   995 {
       
   996     switch ( s ) {
       
   997 	case Q3FtpDTP::CsClosed:
       
   998 	    if ( waitForDtpToClose ) {
       
   999 		// there is an unprocessed reply
       
  1000 		if ( processReply() )
       
  1001 		    replyText = QLatin1String("");
       
  1002 		else
       
  1003 		    return;
       
  1004 	    }
       
  1005 	    waitForDtpToClose = false;
       
  1006 	    readyRead();
       
  1007 	    return;
       
  1008 	case Q3FtpDTP::CsConnected:
       
  1009 	    waitForDtpToConnect = false;
       
  1010 	    startNextCmd();
       
  1011 	    return;
       
  1012 	case Q3FtpDTP::CsHostNotFound:
       
  1013 	case Q3FtpDTP::CsConnectionRefused:
       
  1014 	    emit error( Q3Ftp::ConnectionRefused,
       
  1015 			QFtp::tr( "Connection refused for data connection" ) );
       
  1016 	    startNextCmd();
       
  1017 	    return;
       
  1018 	default:
       
  1019 	    return;
       
  1020     }
       
  1021 }
       
  1022 
       
  1023 /**********************************************************************
       
  1024  *
       
  1025  * Q3FtpPrivate
       
  1026  *
       
  1027  *********************************************************************/
       
  1028 class Q3FtpPrivate
       
  1029 {
       
  1030 public:
       
  1031     Q3FtpPrivate() :
       
  1032 	close_waitForStateChange(false),
       
  1033 	state( Q3Ftp::Unconnected ),
       
  1034 	error( Q3Ftp::NoError ),
       
  1035 	npWaitForLoginDone( false )
       
  1036     { pending.setAutoDelete( true ); }
       
  1037 
       
  1038     Q3FtpPI pi;
       
  1039     Q3PtrList<Q3FtpCommand> pending;
       
  1040     bool close_waitForStateChange;
       
  1041     Q3Ftp::State state;
       
  1042     Q3Ftp::Error error;
       
  1043     QString errorString;
       
  1044 
       
  1045     bool npWaitForLoginDone;
       
  1046 };
       
  1047 
       
  1048 static Q3PtrDict<Q3FtpPrivate> *d_ptr = 0;
       
  1049 static void cleanup_d_ptr()
       
  1050 {
       
  1051     delete d_ptr;
       
  1052     d_ptr = 0;
       
  1053 }
       
  1054 static Q3FtpPrivate* dHelper( const Q3Ftp* foo )
       
  1055 {
       
  1056     if ( !d_ptr ) {
       
  1057 	d_ptr = new Q3PtrDict<Q3FtpPrivate>;
       
  1058 	d_ptr->setAutoDelete( true );
       
  1059 	qAddPostRoutine( cleanup_d_ptr );
       
  1060     }
       
  1061     Q3FtpPrivate* ret = d_ptr->find( (void*)foo );
       
  1062     if ( ! ret ) {
       
  1063 	ret = new Q3FtpPrivate;
       
  1064 	d_ptr->replace( (void*) foo, ret );
       
  1065     }
       
  1066     return ret;
       
  1067 }
       
  1068 
       
  1069 static void delete_d( const Q3Ftp* foo )
       
  1070 {
       
  1071     if ( d_ptr )
       
  1072 	d_ptr->remove( (void*) foo );
       
  1073 }
       
  1074 
       
  1075 /**********************************************************************
       
  1076  *
       
  1077  * Q3Ftp implementation
       
  1078  *
       
  1079  *********************************************************************/
       
  1080 /*!
       
  1081     \class Q3Ftp
       
  1082     \brief The Q3Ftp class provides an implementation of the FTP protocol.
       
  1083 
       
  1084     \compat
       
  1085 
       
  1086     This class provides two different interfaces: one is the
       
  1087     QNetworkProtocol interface that allows you to use FTP through the
       
  1088     QUrlOperator abstraction. The other is a direct interface to FTP
       
  1089     that gives you lower-level access to the FTP protocol for finer
       
  1090     control. Using the direct interface you can also execute arbitrary
       
  1091     FTP commands.
       
  1092 
       
  1093     Don't mix the two interfaces, since the behavior is not
       
  1094     well-defined.
       
  1095 
       
  1096     If you want to use Q3Ftp with the QNetworkProtocol interface, you
       
  1097     do not use it directly, but rather through a QUrlOperator, for
       
  1098     example:
       
  1099 
       
  1100     \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 0
       
  1101 
       
  1102     This code will only work if the Q3Ftp class is registered; to
       
  1103     register the class, you must call q3InitNetworkProtocols() before
       
  1104     using a QUrlOperator with Q3Ftp.
       
  1105 
       
  1106     The rest of this descrption describes the direct interface to FTP.
       
  1107 
       
  1108     The class works asynchronously, so there are no blocking
       
  1109     functions. If an operation cannot be executed immediately, the
       
  1110     function will still return straight away and the operation will be
       
  1111     scheduled for later execution. The results of scheduled operations
       
  1112     are reported via signals. This approach depends on the event loop
       
  1113     being in operation.
       
  1114 
       
  1115     The operations that can be scheduled (they are called "commands"
       
  1116     in the rest of the documentation) are the following:
       
  1117     connectToHost(), login(), close(), list(), cd(), get(), put(),
       
  1118     remove(), mkdir(), rmdir(), rename() and rawCommand().
       
  1119 
       
  1120     All of these commands return a unique identifier that allows you
       
  1121     to keep track of the command that is currently being executed.
       
  1122     When the execution of a command starts, the commandStarted()
       
  1123     signal with the command's identifier is emitted. When the command
       
  1124     is finished, the commandFinished() signal is emitted with the
       
  1125     command's identifier and a bool that indicates whether the command
       
  1126     finished with an error.
       
  1127 
       
  1128     In some cases, you might want to execute a sequence of commands,
       
  1129     e.g. if you want to connect and login to a FTP server. This is
       
  1130     simply achieved:
       
  1131 
       
  1132     \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 1
       
  1133 
       
  1134     In this case two FTP commands have been scheduled. When the last
       
  1135     scheduled command has finished, a done() signal is emitted with
       
  1136     a bool argument that tells you whether the sequence finished with
       
  1137     an error.
       
  1138 
       
  1139     If an error occurs during the execution of one of the commands in
       
  1140     a sequence of commands, all the pending commands (i.e. scheduled,
       
  1141     but not yet executed commands) are cleared and no signals are
       
  1142     emitted for them.
       
  1143 
       
  1144     Some commands, e.g. list(), emit additional signals to report
       
  1145     their results.
       
  1146 
       
  1147     Example: If you want to download the INSTALL file from the Qt
       
  1148     FTP server, you would write this:
       
  1149 
       
  1150     \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 2
       
  1151 
       
  1152     For this example the following sequence of signals is emitted
       
  1153     (with small variations, depending on network traffic, etc.):
       
  1154 
       
  1155     \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 3
       
  1156 
       
  1157     The dataTransferProgress() signal in the above example is useful
       
  1158     if you want to show a \link QProgressBar progress bar \endlink to
       
  1159     inform the user about the progress of the download. The
       
  1160     readyRead() signal tells you that there is data ready to be read.
       
  1161     The amount of data can be queried then with the bytesAvailable()
       
  1162     function and it can be read with the readBlock() or readAll()
       
  1163     function.
       
  1164 
       
  1165     If the login fails for the above example, the signals would look
       
  1166     like this:
       
  1167 
       
  1168     \snippet doc/src/snippets/code/src_qt3support_network_q3ftp.cpp 4
       
  1169 
       
  1170     You can then get details about the error with the error() and
       
  1171     errorString() functions.
       
  1172 
       
  1173     The functions currentId() and currentCommand() provide more
       
  1174     information about the currently executing command.
       
  1175 
       
  1176     The functions hasPendingCommands() and clearPendingCommands()
       
  1177     allow you to query and clear the list of pending commands.
       
  1178 
       
  1179     The safest and easiest way to use the FTP protocol is to use
       
  1180     QUrlOperator() or the FTP commands described above. If you are an
       
  1181     experienced network programmer and want to have complete control
       
  1182     you can use rawCommand() to execute arbitrary FTP commands.
       
  1183 
       
  1184     \sa Q3NetworkProtocol, Q3UrlOperator Q3Http
       
  1185 */
       
  1186 
       
  1187 /*!
       
  1188     Constructs a Q3Ftp object.
       
  1189 */
       
  1190 Q3Ftp::Q3Ftp() : Q3NetworkProtocol()
       
  1191 {
       
  1192     init();
       
  1193 }
       
  1194 
       
  1195 /*!
       
  1196     Constructs a Q3Ftp object. The \a parent and \a name parameters
       
  1197     are passed to the QObject constructor.
       
  1198 */
       
  1199 Q3Ftp::Q3Ftp( QObject *parent, const char *name ) : Q3NetworkProtocol()
       
  1200 {
       
  1201     if ( parent )
       
  1202 	parent->insertChild( this );
       
  1203     setName( name );
       
  1204     init();
       
  1205 }
       
  1206 
       
  1207 void Q3Ftp::init()
       
  1208 {
       
  1209     Q3FtpPrivate *d = dHelper( this );
       
  1210     d->errorString = QFtp::tr( "Unknown error" );
       
  1211 
       
  1212     connect( &d->pi, SIGNAL(connectState(int)),
       
  1213 	    SLOT(piConnectState(int)) );
       
  1214     connect( &d->pi, SIGNAL(finished(QString)),
       
  1215 	    SLOT(piFinished(QString)) );
       
  1216     connect( &d->pi, SIGNAL(error(int,QString)),
       
  1217 	    SLOT(piError(int,QString)) );
       
  1218     connect( &d->pi, SIGNAL(rawFtpReply(int,QString)),
       
  1219 	    SLOT(piFtpReply(int,QString)) );
       
  1220 
       
  1221     connect( &d->pi.dtp, SIGNAL(readyRead()),
       
  1222 	    SIGNAL(readyRead()) );
       
  1223     connect( &d->pi.dtp, SIGNAL(dataTransferProgress(int,int)),
       
  1224 	    SIGNAL(dataTransferProgress(int,int)) );
       
  1225     connect( &d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
       
  1226 	    SIGNAL(listInfo(QUrlInfo)) );
       
  1227 }
       
  1228 
       
  1229 /*!
       
  1230     \enum Q3Ftp::State
       
  1231 
       
  1232     This enum defines the connection state:
       
  1233 
       
  1234     \value Unconnected There is no connection to the host.
       
  1235     \value HostLookup A host name lookup is in progress.
       
  1236     \value Connecting An attempt to connect to the host is in progress.
       
  1237     \value Connected Connection to the host has been achieved.
       
  1238     \value LoggedIn Connection and user login have been achieved.
       
  1239     \value Closing The connection is closing down, but it is not yet
       
  1240     closed. (The state will be \c Unconnected when the connection is
       
  1241     closed.)
       
  1242 
       
  1243     \sa stateChanged() state()
       
  1244 */
       
  1245 /*!
       
  1246     \enum Q3Ftp::Error
       
  1247 
       
  1248     This enum identifies the error that occurred.
       
  1249 
       
  1250     \value NoError No error occurred.
       
  1251     \value HostNotFound The host name lookup failed.
       
  1252     \value ConnectionRefused The server refused the connection.
       
  1253     \value NotConnected Tried to send a command, but there is no connection to
       
  1254     a server.
       
  1255     \value UnknownError An error other than those specified above
       
  1256     occurred.
       
  1257 
       
  1258     \sa error()
       
  1259 */
       
  1260 
       
  1261 /*!
       
  1262     \enum Q3Ftp::Command
       
  1263 
       
  1264     This enum is used as the return value for the currentCommand() function.
       
  1265     This allows you to perform specific actions for particular
       
  1266     commands, e.g. in a FTP client, you might want to clear the
       
  1267     directory view when a list() command is started; in this case you
       
  1268     can simply check in the slot connected to the start() signal if
       
  1269     the currentCommand() is \c List.
       
  1270 
       
  1271     \value None No command is being executed.
       
  1272     \value ConnectToHost connectToHost() is being executed.
       
  1273     \value Login login() is being executed.
       
  1274     \value Close close() is being executed.
       
  1275     \value List list() is being executed.
       
  1276     \value Cd cd() is being executed.
       
  1277     \value Get get() is being executed.
       
  1278     \value Put put() is being executed.
       
  1279     \value Remove remove() is being executed.
       
  1280     \value Mkdir mkdir() is being executed.
       
  1281     \value Rmdir rmdir() is being executed.
       
  1282     \value Rename rename() is being executed.
       
  1283     \value RawCommand rawCommand() is being executed.
       
  1284 
       
  1285     \sa currentCommand()
       
  1286 */
       
  1287 
       
  1288 /*!
       
  1289     \fn void Q3Ftp::stateChanged( int state )
       
  1290 
       
  1291     This signal is emitted when the state of the connection changes.
       
  1292     The argument \a state is the new state of the connection; it is
       
  1293     one of the \l State values.
       
  1294 
       
  1295     It is usually emitted in response to a connectToHost() or close()
       
  1296     command, but it can also be emitted "spontaneously", e.g. when the
       
  1297     server closes the connection unexpectedly.
       
  1298 
       
  1299     \sa connectToHost() close() state() State
       
  1300 */
       
  1301 
       
  1302 /*!
       
  1303     \fn void Q3Ftp::listInfo( const QUrlInfo &i );
       
  1304 
       
  1305     This signal is emitted for each directory entry the list() command
       
  1306     finds. The details of the entry are stored in \a i.
       
  1307 
       
  1308     \sa list()
       
  1309 */
       
  1310 
       
  1311 /*!
       
  1312     \fn void Q3Ftp::commandStarted( int id )
       
  1313 
       
  1314     This signal is emitted when processing the command identified by
       
  1315     \a id starts.
       
  1316 
       
  1317     \sa commandFinished() done()
       
  1318 */
       
  1319 
       
  1320 /*!
       
  1321     \fn void Q3Ftp::commandFinished( int id, bool error )
       
  1322 
       
  1323     This signal is emitted when processing the command identified by
       
  1324     \a id has finished. \a error is true if an error occurred during
       
  1325     the processing; otherwise \a error is false.
       
  1326 
       
  1327     \sa commandStarted() done() error() errorString()
       
  1328 */
       
  1329 
       
  1330 /*!
       
  1331     \fn void Q3Ftp::done( bool error )
       
  1332 
       
  1333     This signal is emitted when the last pending command has finished;
       
  1334     (it is emitted after the last command's commandFinished() signal).
       
  1335     \a error is true if an error occurred during the processing;
       
  1336     otherwise \a error is false.
       
  1337 
       
  1338     \sa commandFinished() error() errorString()
       
  1339 */
       
  1340 
       
  1341 /*!
       
  1342     \fn void Q3Ftp::readyRead()
       
  1343 
       
  1344     This signal is emitted in response to a get() command when there
       
  1345     is new data to read.
       
  1346 
       
  1347     If you specify a device as the second argument in the get()
       
  1348     command, this signal is \e not emitted; instead the data is
       
  1349     written directly to the device.
       
  1350 
       
  1351     You can read the data with the readAll() or readBlock() functions.
       
  1352 
       
  1353     This signal is useful if you want to process the data in chunks as
       
  1354     soon as it becomes available. If you are only interested in the
       
  1355     complete data, just connect to the commandFinished() signal and
       
  1356     read the data then instead.
       
  1357 
       
  1358     \sa get() readBlock() readAll() bytesAvailable()
       
  1359 */
       
  1360 
       
  1361 /*!
       
  1362     \fn void Q3Ftp::dataTransferProgress( int done, int total )
       
  1363 
       
  1364     This signal is emitted in response to a get() or put() request to
       
  1365     indicate the current progress of the download or upload.
       
  1366 
       
  1367     \a done is the amount of data that has already been transferred
       
  1368     and \a total is the total amount of data to be read or written. It
       
  1369     is possible that the Q3Ftp class is not able to determine the total
       
  1370     amount of data that should be transferred, in which case \a total
       
  1371     is 0. (If you connect this signal to a QProgressBar, the progress
       
  1372     bar shows a busy indicator if the total is 0).
       
  1373 
       
  1374     \warning \a done and \a total are not necessarily the size in
       
  1375     bytes, since for large files these values might need to be
       
  1376     "scaled" to avoid overflow.
       
  1377 
       
  1378     \sa get() put()
       
  1379 */
       
  1380 
       
  1381 /*!
       
  1382     \fn void Q3Ftp::rawCommandReply( int replyCode, const QString &detail );
       
  1383 
       
  1384     This signal is emitted in response to the rawCommand() function.
       
  1385     \a replyCode is the 3 digit reply code and \a detail is the text
       
  1386     that follows the reply code.
       
  1387 
       
  1388     \sa rawCommand()
       
  1389 */
       
  1390 
       
  1391 /*!
       
  1392     Connects to the FTP server \a host using port \a port.
       
  1393 
       
  1394     The stateChanged() signal is emitted when the state of the
       
  1395     connecting process changes, e.g. to \c HostLookup, then \c
       
  1396     Connecting, then \c Connected.
       
  1397 
       
  1398     The function does not block and returns immediately. The command
       
  1399     is scheduled, and its execution is performed asynchronously. The
       
  1400     function returns a unique identifier which is passed by
       
  1401     commandStarted() and commandFinished().
       
  1402 
       
  1403     When the command is started the commandStarted() signal is
       
  1404     emitted. When it is finished the commandFinished() signal is
       
  1405     emitted.
       
  1406 
       
  1407     \sa stateChanged() commandStarted() commandFinished()
       
  1408 */
       
  1409 int Q3Ftp::connectToHost( const QString &host, Q_UINT16 port )
       
  1410 {
       
  1411     QStringList cmds;
       
  1412     cmds << host;
       
  1413     cmds << QString::number( (uint)port );
       
  1414     return addCommand( new Q3FtpCommand( ConnectToHost, cmds ) );
       
  1415 }
       
  1416 
       
  1417 /*!
       
  1418     Logs in to the FTP server with the username \a user and the
       
  1419     password \a password.
       
  1420 
       
  1421     The stateChanged() signal is emitted when the state of the
       
  1422     connecting process changes, e.g. to \c LoggedIn.
       
  1423 
       
  1424     The function does not block and returns immediately. The command
       
  1425     is scheduled, and its execution is performed asynchronously. The
       
  1426     function returns a unique identifier which is passed by
       
  1427     commandStarted() and commandFinished().
       
  1428 
       
  1429     When the command is started the commandStarted() signal is
       
  1430     emitted. When it is finished the commandFinished() signal is
       
  1431     emitted.
       
  1432 
       
  1433     \sa commandStarted() commandFinished()
       
  1434 */
       
  1435 int Q3Ftp::login( const QString &user, const QString &password )
       
  1436 {
       
  1437     QStringList cmds;
       
  1438     cmds << ( QString::fromLatin1("USER ") + ( user.isNull() ? QString::fromLatin1("anonymous") : user ) + QLatin1String("\r\n") );
       
  1439     cmds << ( QString::fromLatin1("PASS ") + ( password.isNull() ? QString::fromLatin1("anonymous@") : password ) + QLatin1String("\r\n") );
       
  1440     return addCommand( new Q3FtpCommand( Login, cmds ) );
       
  1441 }
       
  1442 
       
  1443 /*!
       
  1444     Closes the connection to the FTP server.
       
  1445 
       
  1446     The stateChanged() signal is emitted when the state of the
       
  1447     connecting process changes, e.g. to \c Closing, then \c
       
  1448     Unconnected.
       
  1449 
       
  1450     The function does not block and returns immediately. The command
       
  1451     is scheduled, and its execution is performed asynchronously. The
       
  1452     function returns a unique identifier which is passed by
       
  1453     commandStarted() and commandFinished().
       
  1454 
       
  1455     When the command is started the commandStarted() signal is
       
  1456     emitted. When it is finished the commandFinished() signal is
       
  1457     emitted.
       
  1458 
       
  1459     \sa stateChanged() commandStarted() commandFinished()
       
  1460 */
       
  1461 int Q3Ftp::close()
       
  1462 {
       
  1463     return addCommand( new Q3FtpCommand( Close, QStringList(QLatin1String("QUIT\r\n")) ) );
       
  1464 }
       
  1465 
       
  1466 /*!
       
  1467     Lists the contents of directory \a dir on the FTP server. If \a
       
  1468     dir is empty, it lists the contents of the current directory.
       
  1469 
       
  1470     The listInfo() signal is emitted for each directory entry found.
       
  1471 
       
  1472     The function does not block and returns immediately. The command
       
  1473     is scheduled, and its execution is performed asynchronously. The
       
  1474     function returns a unique identifier which is passed by
       
  1475     commandStarted() and commandFinished().
       
  1476 
       
  1477     When the command is started the commandStarted() signal is
       
  1478     emitted. When it is finished the commandFinished() signal is
       
  1479     emitted.
       
  1480 
       
  1481     \sa listInfo() commandStarted() commandFinished()
       
  1482 */
       
  1483 int Q3Ftp::list( const QString &dir )
       
  1484 {
       
  1485     QStringList cmds;
       
  1486     cmds << QLatin1String("TYPE A\r\n");
       
  1487     cmds << QLatin1String("PASV\r\n");
       
  1488     if ( dir.isEmpty() )
       
  1489 	cmds << QLatin1String("LIST\r\n");
       
  1490     else
       
  1491 	cmds << ( QLatin1String("LIST ") + dir + QLatin1String("\r\n") );
       
  1492     return addCommand( new Q3FtpCommand( List, cmds ) );
       
  1493 }
       
  1494 
       
  1495 /*!
       
  1496     Changes the working directory of the server to \a dir.
       
  1497 
       
  1498     The function does not block and returns immediately. The command
       
  1499     is scheduled, and its execution is performed asynchronously. The
       
  1500     function returns a unique identifier which is passed by
       
  1501     commandStarted() and commandFinished().
       
  1502 
       
  1503     When the command is started the commandStarted() signal is
       
  1504     emitted. When it is finished the commandFinished() signal is
       
  1505     emitted.
       
  1506 
       
  1507     \sa commandStarted() commandFinished()
       
  1508 */
       
  1509 int Q3Ftp::cd( const QString &dir )
       
  1510 {
       
  1511     return addCommand( new Q3FtpCommand( Cd, QStringList(QLatin1String("CWD ")+dir+QLatin1String("\r\n")) ) );
       
  1512 }
       
  1513 
       
  1514 /*!
       
  1515     Downloads the file \a file from the server.
       
  1516 
       
  1517     If \a dev is 0, then the readyRead() signal is emitted when there
       
  1518     is data available to read. You can then read the data with the
       
  1519     readBlock() or readAll() functions.
       
  1520 
       
  1521     If \a dev is not 0, the data is written directly to the device \a
       
  1522     dev. Make sure that the \a dev pointer is valid for the duration
       
  1523     of the operation (it is safe to delete it when the
       
  1524     commandFinished() signal is emitted). In this case the readyRead()
       
  1525     signal is \e not emitted and you cannot read data with the
       
  1526     readBlock() or readAll() functions.
       
  1527 
       
  1528     If you don't read the data immediately it becomes available, i.e.
       
  1529     when the readyRead() signal is emitted, it is still available
       
  1530     until the next command is started.
       
  1531 
       
  1532     For example, if you want to present the data to the user as soon
       
  1533     as there is something available, connect to the readyRead() signal
       
  1534     and read the data immediately. On the other hand, if you only want
       
  1535     to work with the complete data, you can connect to the
       
  1536     commandFinished() signal and read the data when the get() command
       
  1537     is finished.
       
  1538 
       
  1539     The function does not block and returns immediately. The command
       
  1540     is scheduled, and its execution is performed asynchronously. The
       
  1541     function returns a unique identifier which is passed by
       
  1542     commandStarted() and commandFinished().
       
  1543 
       
  1544     When the command is started the commandStarted() signal is
       
  1545     emitted. When it is finished the commandFinished() signal is
       
  1546     emitted.
       
  1547 
       
  1548     \sa readyRead() dataTransferProgress() commandStarted()
       
  1549     commandFinished()
       
  1550 */
       
  1551 int Q3Ftp::get( const QString &file, QIODevice *dev )
       
  1552 {
       
  1553     QStringList cmds;
       
  1554     cmds << ( QLatin1String("SIZE ") + file + QLatin1String("\r\n") );
       
  1555     cmds << QLatin1String("TYPE I\r\n");
       
  1556     cmds << QLatin1String("PASV\r\n");
       
  1557     cmds << ( QLatin1String("RETR ") + file + QLatin1String("\r\n") );
       
  1558     if ( dev )
       
  1559 	return addCommand( new Q3FtpCommand( Get, cmds, dev ) );
       
  1560     return addCommand( new Q3FtpCommand( Get, cmds ) );
       
  1561 }
       
  1562 
       
  1563 /*!
       
  1564     \overload
       
  1565 
       
  1566     Writes the data \a data to the file called \a file on the server.
       
  1567     The progress of the upload is reported by the
       
  1568     dataTransferProgress() signal.
       
  1569 
       
  1570     The function does not block and returns immediately. The command
       
  1571     is scheduled, and its execution is performed asynchronously. The
       
  1572     function returns a unique identifier which is passed by
       
  1573     commandStarted() and commandFinished().
       
  1574 
       
  1575     When the command is started the commandStarted() signal is
       
  1576     emitted. When it is finished the commandFinished() signal is
       
  1577     emitted.
       
  1578 
       
  1579     \sa dataTransferProgress() commandStarted() commandFinished()
       
  1580 */
       
  1581 int Q3Ftp::put( const QByteArray &data, const QString &file )
       
  1582 {
       
  1583     QStringList cmds;
       
  1584     cmds << QLatin1String("TYPE I\r\n");
       
  1585     cmds << QLatin1String("PASV\r\n");
       
  1586     cmds << ( QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n") );
       
  1587     cmds << ( QLatin1String("STOR ") + file + QLatin1String("\r\n") );
       
  1588     return addCommand( new Q3FtpCommand( Put, cmds, data ) );
       
  1589 }
       
  1590 
       
  1591 /*!
       
  1592     Reads the data from the IO device \a dev, and writes it to the
       
  1593     file called \a file on the server. The data is read in chunks from
       
  1594     the IO device, so this overload allows you to transmit large
       
  1595     amounts of data without the need to read all the data into memory
       
  1596     at once.
       
  1597 
       
  1598     Make sure that the \a dev pointer is valid for the duration of the
       
  1599     operation (it is safe to delete it when the commandFinished() is
       
  1600     emitted).
       
  1601 */
       
  1602 int Q3Ftp::put( QIODevice *dev, const QString &file )
       
  1603 {
       
  1604     QStringList cmds;
       
  1605     cmds << QLatin1String("TYPE I\r\n");
       
  1606     cmds << QLatin1String("PASV\r\n");
       
  1607     if ( !dev->isSequentialAccess() )
       
  1608 	cmds << ( QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n") );
       
  1609     cmds << ( QLatin1String("STOR ") + file + QLatin1String("\r\n") );
       
  1610     return addCommand( new Q3FtpCommand( Put, cmds, dev ) );
       
  1611 }
       
  1612 
       
  1613 /*!
       
  1614     Deletes the file called \a file from the server.
       
  1615 
       
  1616     The function does not block and returns immediately. The command
       
  1617     is scheduled, and its execution is performed asynchronously. The
       
  1618     function returns a unique identifier which is passed by
       
  1619     commandStarted() and commandFinished().
       
  1620 
       
  1621     When the command is started the commandStarted() signal is
       
  1622     emitted. When it is finished the commandFinished() signal is
       
  1623     emitted.
       
  1624 
       
  1625     \sa commandStarted() commandFinished()
       
  1626 */
       
  1627 int Q3Ftp::remove( const QString &file )
       
  1628 {
       
  1629     return addCommand( new Q3FtpCommand( Remove, QStringList(QLatin1String("DELE ")+file+QLatin1String("\r\n")) ) );
       
  1630 }
       
  1631 
       
  1632 /*!
       
  1633     Creates a directory called \a dir on the server.
       
  1634 
       
  1635     The function does not block and returns immediately. The command
       
  1636     is scheduled, and its execution is performed asynchronously. The
       
  1637     function returns a unique identifier which is passed by
       
  1638     commandStarted() and commandFinished().
       
  1639 
       
  1640     When the command is started the commandStarted() signal is
       
  1641     emitted. When it is finished the commandFinished() signal is
       
  1642     emitted.
       
  1643 
       
  1644     \sa commandStarted() commandFinished()
       
  1645 */
       
  1646 int Q3Ftp::mkdir( const QString &dir )
       
  1647 {
       
  1648     return addCommand( new Q3FtpCommand( Mkdir, QStringList(QLatin1String("MKD ")+dir+QLatin1String("\r\n")) ) );
       
  1649 }
       
  1650 
       
  1651 /*!
       
  1652     Removes the directory called \a dir from the server.
       
  1653 
       
  1654     The function does not block and returns immediately. The command
       
  1655     is scheduled, and its execution is performed asynchronously. The
       
  1656     function returns a unique identifier which is passed by
       
  1657     commandStarted() and commandFinished().
       
  1658 
       
  1659     When the command is started the commandStarted() signal is
       
  1660     emitted. When it is finished the commandFinished() signal is
       
  1661     emitted.
       
  1662 
       
  1663     \sa commandStarted() commandFinished()
       
  1664 */
       
  1665 int Q3Ftp::rmdir( const QString &dir )
       
  1666 {
       
  1667     return addCommand( new Q3FtpCommand( Rmdir, QStringList(QLatin1String("RMD ")+dir+QLatin1String("\r\n")) ) );
       
  1668 }
       
  1669 
       
  1670 /*!
       
  1671     Renames the file called \a oldname to \a newname on the server.
       
  1672 
       
  1673     The function does not block and returns immediately. The command
       
  1674     is scheduled, and its execution is performed asynchronously. The
       
  1675     function returns a unique identifier which is passed by
       
  1676     commandStarted() and commandFinished().
       
  1677 
       
  1678     When the command is started the commandStarted() signal is
       
  1679     emitted. When it is finished the commandFinished() signal is
       
  1680     emitted.
       
  1681 
       
  1682     \sa commandStarted() commandFinished()
       
  1683 */
       
  1684 int Q3Ftp::rename( const QString &oldname, const QString &newname )
       
  1685 {
       
  1686     QStringList cmds;
       
  1687     cmds << ( QLatin1String("RNFR ") + oldname + QLatin1String("\r\n") );
       
  1688     cmds << ( QLatin1String("RNTO ") + newname + QLatin1String("\r\n") );
       
  1689     return addCommand( new Q3FtpCommand( Rename, cmds ) );
       
  1690 }
       
  1691 
       
  1692 /*!
       
  1693     Sends the raw FTP command \a command to the FTP server. This is
       
  1694     useful for low-level FTP access. If the operation you wish to
       
  1695     perform has an equivalent Q3Ftp function, we recommend using the
       
  1696     function instead of raw FTP commands since the functions are
       
  1697     easier and safer.
       
  1698 
       
  1699     The function does not block and returns immediately. The command
       
  1700     is scheduled, and its execution is performed asynchronously. The
       
  1701     function returns a unique identifier which is passed by
       
  1702     commandStarted() and commandFinished().
       
  1703 
       
  1704     When the command is started the commandStarted() signal is
       
  1705     emitted. When it is finished the commandFinished() signal is
       
  1706     emitted.
       
  1707 
       
  1708     \sa rawCommandReply() commandStarted() commandFinished()
       
  1709 */
       
  1710 int Q3Ftp::rawCommand( const QString &command )
       
  1711 {
       
  1712     QString cmd = command.stripWhiteSpace() + QLatin1String("\r\n");
       
  1713     return addCommand( new Q3FtpCommand( RawCommand, QStringList(cmd) ) );
       
  1714 }
       
  1715 
       
  1716 /*!
       
  1717     Returns the number of bytes that can be read from the data socket
       
  1718     at the moment.
       
  1719 
       
  1720     \sa get() readyRead() readBlock() readAll()
       
  1721 */
       
  1722 Q_ULONG Q3Ftp::bytesAvailable() const
       
  1723 {
       
  1724     Q3FtpPrivate *d = dHelper( this );
       
  1725     return d->pi.dtp.bytesAvailable();
       
  1726 }
       
  1727 
       
  1728 /*!
       
  1729     Reads \a maxlen bytes from the data socket into \a data and
       
  1730     returns the number of bytes read. Returns -1 if an error occurred.
       
  1731 
       
  1732     \sa get() readyRead() bytesAvailable() readAll()
       
  1733 */
       
  1734 Q_LONG Q3Ftp::readBlock( char *data, Q_ULONG maxlen )
       
  1735 {
       
  1736     Q3FtpPrivate *d = dHelper( this );
       
  1737     return d->pi.dtp.readBlock( data, maxlen );
       
  1738 }
       
  1739 
       
  1740 /*!
       
  1741     Reads all the bytes available from the data socket and returns
       
  1742     them.
       
  1743 
       
  1744     \sa get() readyRead() bytesAvailable() readBlock()
       
  1745 */
       
  1746 QByteArray Q3Ftp::readAll()
       
  1747 {
       
  1748     Q3FtpPrivate *d = dHelper( this );
       
  1749     return d->pi.dtp.readAll();
       
  1750 }
       
  1751 
       
  1752 /*!
       
  1753     Aborts the current command and deletes all scheduled commands.
       
  1754 
       
  1755     If there is an unfinished command (i.e. a command for which the
       
  1756     commandStarted() signal has been emitted, but for which the
       
  1757     commandFinished() signal has not been emitted), this function
       
  1758     sends an \c ABORT command to the server. When the server replies
       
  1759     that the command is aborted, the commandFinished() signal with the
       
  1760     \c error argument set to \c true is emitted for the command. Due
       
  1761     to timing issues, it is possible that the command had already
       
  1762     finished before the abort request reached the server, in which
       
  1763     case, the commandFinished() signal is emitted with the \c error
       
  1764     argument set to \c false.
       
  1765 
       
  1766     For all other commands that are affected by the abort(), no
       
  1767     signals are emitted.
       
  1768 
       
  1769     If you don't start further FTP commands directly after the
       
  1770     abort(), there won't be any scheduled commands and the done()
       
  1771     signal is emitted.
       
  1772 
       
  1773     \warning Some FTP servers, for example the BSD FTP daemon (version
       
  1774     0.3), wrongly return a positive reply even when an abort has
       
  1775     occurred. For these servers the commandFinished() signal has its
       
  1776     error flag set to \c false, even though the command did not
       
  1777     complete successfully.
       
  1778 
       
  1779     \sa clearPendingCommands()
       
  1780 */
       
  1781 void Q3Ftp::abort()
       
  1782 {
       
  1783     Q3FtpPrivate *d = dHelper( this );
       
  1784     if ( d->pending.isEmpty() )
       
  1785 	return;
       
  1786 
       
  1787     clearPendingCommands();
       
  1788     d->pi.abort();
       
  1789 }
       
  1790 
       
  1791 /*!
       
  1792     Returns the identifier of the FTP command that is being executed
       
  1793     or 0 if there is no command being executed.
       
  1794 
       
  1795     \sa currentCommand()
       
  1796 */
       
  1797 int Q3Ftp::currentId() const
       
  1798 {
       
  1799     Q3FtpPrivate *d = dHelper( this );
       
  1800     Q3FtpCommand *c = d->pending.getFirst();
       
  1801     if ( c == 0 )
       
  1802 	return 0;
       
  1803     return c->id;
       
  1804 }
       
  1805 
       
  1806 /*!
       
  1807     Returns the command type of the FTP command being executed or \c
       
  1808     None if there is no command being executed.
       
  1809 
       
  1810     \sa currentId()
       
  1811 */
       
  1812 Q3Ftp::Command Q3Ftp::currentCommand() const
       
  1813 {
       
  1814     Q3FtpPrivate *d = dHelper( this );
       
  1815     Q3FtpCommand *c = d->pending.getFirst();
       
  1816     if ( c == 0 )
       
  1817 	return None;
       
  1818     return c->command;
       
  1819 }
       
  1820 
       
  1821 /*!
       
  1822     Returns the QIODevice pointer that is used by the FTP command to read data
       
  1823     from or store data to. If there is no current FTP command being executed or
       
  1824     if the command does not use an IO device, this function returns 0.
       
  1825 
       
  1826     This function can be used to delete the QIODevice in the slot connected to
       
  1827     the commandFinished() signal.
       
  1828 
       
  1829     \sa get() put()
       
  1830 */
       
  1831 QIODevice* Q3Ftp::currentDevice() const
       
  1832 {
       
  1833     Q3FtpPrivate *d = dHelper( this );
       
  1834     Q3FtpCommand *c = d->pending.getFirst();
       
  1835     if ( !c )
       
  1836 	return 0;
       
  1837     if ( c->is_ba )
       
  1838 	return 0;
       
  1839     return c->data.dev;
       
  1840 }
       
  1841 
       
  1842 /*!
       
  1843     Returns true if there are any commands scheduled that have not yet
       
  1844     been executed; otherwise returns false.
       
  1845 
       
  1846     The command that is being executed is \e not considered as a
       
  1847     scheduled command.
       
  1848 
       
  1849     \sa clearPendingCommands() currentId() currentCommand()
       
  1850 */
       
  1851 bool Q3Ftp::hasPendingCommands() const
       
  1852 {
       
  1853     Q3FtpPrivate *d = dHelper( this );
       
  1854     return d->pending.count() > 1;
       
  1855 }
       
  1856 
       
  1857 /*!
       
  1858     Deletes all pending commands from the list of scheduled commands.
       
  1859     This does not affect the command that is being executed. If you
       
  1860     want to stop this as well, use abort().
       
  1861 
       
  1862     \sa hasPendingCommands() abort()
       
  1863 */
       
  1864 void Q3Ftp::clearPendingCommands()
       
  1865 {
       
  1866     Q3FtpPrivate *d = dHelper( this );
       
  1867     Q3FtpCommand *c = 0;
       
  1868     if ( d->pending.count() > 0 )
       
  1869 	c = d->pending.take( 0 );
       
  1870     d->pending.clear();
       
  1871     if ( c )
       
  1872 	d->pending.append( c );
       
  1873 }
       
  1874 
       
  1875 /*!
       
  1876     Returns the current state of the object. When the state changes,
       
  1877     the stateChanged() signal is emitted.
       
  1878 
       
  1879     \sa State stateChanged()
       
  1880 */
       
  1881 Q3Ftp::State Q3Ftp::state() const
       
  1882 {
       
  1883     Q3FtpPrivate *d = dHelper( this );
       
  1884     return d->state;
       
  1885 }
       
  1886 
       
  1887 /*!
       
  1888     Returns the last error that occurred. This is useful to find out
       
  1889     what when wrong when receiving a commandFinished() or a done()
       
  1890     signal with the \c error argument set to \c true.
       
  1891 
       
  1892     If you start a new command, the error status is reset to \c NoError.
       
  1893 */
       
  1894 Q3Ftp::Error Q3Ftp::error() const
       
  1895 {
       
  1896     Q3FtpPrivate *d = dHelper( this );
       
  1897     return d->error;
       
  1898 }
       
  1899 
       
  1900 /*!
       
  1901     Returns a human-readable description of the last error that
       
  1902     occurred. This is useful for presenting a error message to the
       
  1903     user when receiving a commandFinished() or a done() signal with
       
  1904     the \c error argument set to \c true.
       
  1905 
       
  1906     The error string is often (but not always) the reply from the
       
  1907     server, so it is not always possible to translate the string. If
       
  1908     the message comes from Qt, the string has already passed through
       
  1909     tr().
       
  1910 */
       
  1911 QString Q3Ftp::errorString() const
       
  1912 {
       
  1913     Q3FtpPrivate *d = dHelper( this );
       
  1914     return d->errorString;
       
  1915 }
       
  1916 
       
  1917 int Q3Ftp::addCommand( Q3FtpCommand *cmd )
       
  1918 {
       
  1919     Q3FtpPrivate *d = dHelper( this );
       
  1920     d->pending.append( cmd );
       
  1921 
       
  1922     if ( d->pending.count() == 1 )
       
  1923 	// don't emit the commandStarted() signal before the id is returned
       
  1924 	QTimer::singleShot( 0, this, SLOT(startNextCommand()) );
       
  1925 
       
  1926     return cmd->id;
       
  1927 }
       
  1928 
       
  1929 void Q3Ftp::startNextCommand()
       
  1930 {
       
  1931     Q3FtpPrivate *d = dHelper( this );
       
  1932 
       
  1933     Q3FtpCommand *c = d->pending.getFirst();
       
  1934     if ( c == 0 )
       
  1935 	return;
       
  1936 
       
  1937     d->error = NoError;
       
  1938     d->errorString = QFtp::tr( "Unknown error" );
       
  1939 
       
  1940     if ( bytesAvailable() )
       
  1941 	readAll(); // clear the data
       
  1942     emit commandStarted( c->id );
       
  1943 
       
  1944     if ( c->command == ConnectToHost ) {
       
  1945 	d->pi.connectToHost( c->rawCmds[0], c->rawCmds[1].toUInt() );
       
  1946     } else {
       
  1947 	if ( c->command == Put ) {
       
  1948 	    if ( c->is_ba ) {
       
  1949 		d->pi.dtp.setData( c->data.ba );
       
  1950 		d->pi.dtp.setBytesTotal( c->data.ba->size() );
       
  1951 	    } else if ( c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly)) ) {
       
  1952 		d->pi.dtp.setDevice( c->data.dev );
       
  1953 		if ( c->data.dev->isSequentialAccess() )
       
  1954 		    d->pi.dtp.setBytesTotal( 0 );
       
  1955 		else
       
  1956 		    d->pi.dtp.setBytesTotal( c->data.dev->size() );
       
  1957 	    }
       
  1958 	} else if ( c->command == Get ) {
       
  1959 	    if ( !c->is_ba && c->data.dev ) {
       
  1960 		d->pi.dtp.setDevice( c->data.dev );
       
  1961 	    }
       
  1962 	} else if ( c->command == Close ) {
       
  1963 	    d->state = Q3Ftp::Closing;
       
  1964 	    emit stateChanged( d->state );
       
  1965 	}
       
  1966 	if ( !d->pi.sendCommands( c->rawCmds ) ) {
       
  1967 	    // ### error handling (this case should not happen)
       
  1968 	}
       
  1969     }
       
  1970 }
       
  1971 
       
  1972 void Q3Ftp::piFinished( const QString& )
       
  1973 {
       
  1974     Q3FtpPrivate *d = dHelper( this );
       
  1975     Q3FtpCommand *c = d->pending.getFirst();
       
  1976     if ( c == 0 )
       
  1977 	return;
       
  1978 
       
  1979     if ( c->command == Close ) {
       
  1980 	// The order of in which the slots are called is arbitrary, so
       
  1981 	// disconnect the SIGNAL-SIGNAL temporary to make sure that we
       
  1982 	// don't get the commandFinished() signal before the stateChanged()
       
  1983 	// signal.
       
  1984 	if ( d->state != Q3Ftp::Unconnected ) {
       
  1985 	    d->close_waitForStateChange = true;
       
  1986 	    return;
       
  1987 	}
       
  1988     }
       
  1989     emit commandFinished( c->id, false );
       
  1990 
       
  1991     d->pending.removeFirst();
       
  1992     if ( d->pending.isEmpty() ) {
       
  1993 	emit done( false );
       
  1994     } else {
       
  1995 	startNextCommand();
       
  1996     }
       
  1997 }
       
  1998 
       
  1999 void Q3Ftp::piError( int errorCode, const QString &text )
       
  2000 {
       
  2001     Q3FtpPrivate *d = dHelper( this );
       
  2002     Q3FtpCommand *c = d->pending.getFirst();
       
  2003 
       
  2004     // non-fatal errors
       
  2005     if ( c->command==Get && d->pi.currentCommand().startsWith(QLatin1String("SIZE ")) ) {
       
  2006 	d->pi.dtp.setBytesTotal( -1 );
       
  2007 	return;
       
  2008     } else if ( c->command==Put && d->pi.currentCommand().startsWith(QLatin1String("ALLO ")) ) {
       
  2009 	return;
       
  2010     }
       
  2011 
       
  2012     d->error = (Error)errorCode;
       
  2013     switch ( currentCommand() ) {
       
  2014 	case ConnectToHost:
       
  2015 	    d->errorString = QFtp::tr( "Connecting to host failed:\n%1" ).arg( text );
       
  2016 	    break;
       
  2017 	case Login:
       
  2018 	    d->errorString = QFtp::tr( "Login failed:\n%1" ).arg( text );
       
  2019 	    break;
       
  2020 	case List:
       
  2021 	    d->errorString = QFtp::tr( "Listing directory failed:\n%1" ).arg( text );
       
  2022 	    break;
       
  2023 	case Cd:
       
  2024 	    d->errorString = QFtp::tr( "Changing directory failed:\n%1" ).arg( text );
       
  2025 	    break;
       
  2026 	case Get:
       
  2027 	    d->errorString = QFtp::tr( "Downloading file failed:\n%1" ).arg( text );
       
  2028 	    break;
       
  2029 	case Put:
       
  2030 	    d->errorString = QFtp::tr( "Uploading file failed:\n%1" ).arg( text );
       
  2031 	    break;
       
  2032 	case Remove:
       
  2033 	    d->errorString = QFtp::tr( "Removing file failed:\n%1" ).arg( text );
       
  2034 	    break;
       
  2035 	case Mkdir:
       
  2036 	    d->errorString = QFtp::tr( "Creating directory failed:\n%1" ).arg( text );
       
  2037 	    break;
       
  2038 	case Rmdir:
       
  2039 	    d->errorString = QFtp::tr( "Removing directory failed:\n%1" ).arg( text );
       
  2040 	    break;
       
  2041 	default:
       
  2042 	    d->errorString = text;
       
  2043 	    break;
       
  2044     }
       
  2045 
       
  2046     d->pi.clearPendingCommands();
       
  2047     clearPendingCommands();
       
  2048     emit commandFinished( c->id, true );
       
  2049 
       
  2050     d->pending.removeFirst();
       
  2051     if ( d->pending.isEmpty() )
       
  2052 	emit done( true );
       
  2053     else
       
  2054 	startNextCommand();
       
  2055 }
       
  2056 
       
  2057 void Q3Ftp::piConnectState( int state )
       
  2058 {
       
  2059     Q3FtpPrivate *d = dHelper( this );
       
  2060     d->state = (State)state;
       
  2061     emit stateChanged( d->state );
       
  2062     if ( d->close_waitForStateChange ) {
       
  2063 	d->close_waitForStateChange = false;
       
  2064 	piFinished( QFtp::tr( "Connection closed" ) );
       
  2065     }
       
  2066 }
       
  2067 
       
  2068 void Q3Ftp::piFtpReply( int code, const QString &text )
       
  2069 {
       
  2070     if ( currentCommand() == RawCommand ) {
       
  2071 	Q3FtpPrivate *d = dHelper( this );
       
  2072 	d->pi.rawCommand = true;
       
  2073 	emit rawCommandReply( code, text );
       
  2074     }
       
  2075 }
       
  2076 
       
  2077 /*!
       
  2078     Destructor.
       
  2079 */
       
  2080 Q3Ftp::~Q3Ftp()
       
  2081 {
       
  2082     abort();
       
  2083     close();
       
  2084     delete_d( this );
       
  2085 }
       
  2086 
       
  2087 /**********************************************************************
       
  2088  *
       
  2089  * Q3Ftp implementation of the Q3NetworkProtocol interface
       
  2090  *
       
  2091  *********************************************************************/
       
  2092 /*!  \reimp
       
  2093 */
       
  2094 void Q3Ftp::operationListChildren( Q3NetworkOperation *op )
       
  2095 {
       
  2096     op->setState( StInProgress );
       
  2097 
       
  2098     cd( ( url()->path().isEmpty() ? QString::fromLatin1("/") : url()->path() ) );
       
  2099     list();
       
  2100     emit start( op );
       
  2101 }
       
  2102 
       
  2103 /*!  \reimp
       
  2104 */
       
  2105 void Q3Ftp::operationMkDir( Q3NetworkOperation *op )
       
  2106 {
       
  2107     op->setState( StInProgress );
       
  2108 
       
  2109     mkdir( op->arg( 0 ) );
       
  2110 }
       
  2111 
       
  2112 /*!  \reimp
       
  2113 */
       
  2114 void Q3Ftp::operationRemove( Q3NetworkOperation *op )
       
  2115 {
       
  2116     op->setState( StInProgress );
       
  2117 
       
  2118     cd( ( url()->path().isEmpty() ? QString::fromLatin1("/") : url()->path() ) );
       
  2119     remove( Q3Url( op->arg( 0 ) ).path() );
       
  2120 }
       
  2121 
       
  2122 /*!  \reimp
       
  2123 */
       
  2124 void Q3Ftp::operationRename( Q3NetworkOperation *op )
       
  2125 {
       
  2126     op->setState( StInProgress );
       
  2127 
       
  2128     cd( ( url()->path().isEmpty() ? QString::fromLatin1("/") : url()->path() ) );
       
  2129     rename( op->arg( 0 ), op->arg( 1 ));
       
  2130 }
       
  2131 
       
  2132 /*!  \reimp
       
  2133 */
       
  2134 void Q3Ftp::operationGet( Q3NetworkOperation *op )
       
  2135 {
       
  2136     op->setState( StInProgress );
       
  2137 
       
  2138     Q3Url u( op->arg( 0 ) );
       
  2139     get( u.path() );
       
  2140 }
       
  2141 
       
  2142 /*!  \reimp
       
  2143 */
       
  2144 void Q3Ftp::operationPut( Q3NetworkOperation *op )
       
  2145 {
       
  2146     op->setState( StInProgress );
       
  2147 
       
  2148     Q3Url u( op->arg( 0 ) );
       
  2149     put( op->rawArg(1), u.path() );
       
  2150 }
       
  2151 
       
  2152 /*!  \reimp
       
  2153 */
       
  2154 bool Q3Ftp::checkConnection( Q3NetworkOperation *op )
       
  2155 {
       
  2156     Q3FtpPrivate *d = dHelper( this );
       
  2157     if ( state() == Unconnected && !d->npWaitForLoginDone ) {
       
  2158 	connect( this, SIGNAL(listInfo(QUrlInfo)),
       
  2159 		this, SLOT(npListInfo(QUrlInfo)) );
       
  2160 	connect( this, SIGNAL(done(bool)),
       
  2161 		this, SLOT(npDone(bool)) );
       
  2162 	connect( this, SIGNAL(stateChanged(int)),
       
  2163 		this, SLOT(npStateChanged(int)) );
       
  2164 	connect( this, SIGNAL(dataTransferProgress(int,int)),
       
  2165 		this, SLOT(npDataTransferProgress(int,int)) );
       
  2166 	connect( this, SIGNAL(readyRead()),
       
  2167 		this, SLOT(npReadyRead()) );
       
  2168 
       
  2169 	d->npWaitForLoginDone = true;
       
  2170 	switch ( op->operation() ) {
       
  2171 	    case OpGet:
       
  2172 	    case OpPut:
       
  2173 		{
       
  2174 		    Q3Url u( op->arg( 0 ) );
       
  2175 		    connectToHost( u.host(), u.port() != -1 ? u.port() : 21 );
       
  2176 		}
       
  2177 		break;
       
  2178 	    default:
       
  2179 		connectToHost( url()->host(), url()->port() != -1 ? url()->port() : 21 );
       
  2180 		break;
       
  2181 	}
       
  2182     QString user = url()->user().isEmpty() ? QString::fromLatin1("anonymous") : url()->user();
       
  2183     QString pass = url()->password().isEmpty() ? QString::fromLatin1("anonymous@") : url()->password();
       
  2184 	login( user, pass );
       
  2185     }
       
  2186 
       
  2187     if ( state() == LoggedIn )
       
  2188 	return true;
       
  2189     return false;
       
  2190 }
       
  2191 
       
  2192 /*!  \reimp
       
  2193 */
       
  2194 int Q3Ftp::supportedOperations() const
       
  2195 {
       
  2196     return OpListChildren | OpMkDir | OpRemove | OpRename | OpGet | OpPut;
       
  2197 }
       
  2198 
       
  2199 /*! \internal
       
  2200     Parses the string, \a buffer, which is one line of a directory
       
  2201     listing which came from the FTP server, and sets the values which
       
  2202     have been parsed to the url info object, \a info.
       
  2203 */
       
  2204 void Q3Ftp::parseDir( const QString &buffer, QUrlInfo &info )
       
  2205 {
       
  2206     Q3FtpDTP::parseDir( buffer, url()->user(), &info );
       
  2207 }
       
  2208 
       
  2209 void Q3Ftp::npListInfo( const QUrlInfo & i )
       
  2210 {
       
  2211     if ( url() ) {
       
  2212 	QRegExp filt( url()->nameFilter(), false, true );
       
  2213 	if ( i.isDir() || filt.search( i.name() ) != -1 ) {
       
  2214 	    emit newChild( i, operationInProgress() );
       
  2215 	}
       
  2216     } else {
       
  2217 	emit newChild( i, operationInProgress() );
       
  2218     }
       
  2219 }
       
  2220 
       
  2221 void Q3Ftp::npDone( bool err )
       
  2222 {
       
  2223     Q3FtpPrivate *d = dHelper( this );
       
  2224 
       
  2225     bool emitFinishedSignal = false;
       
  2226     Q3NetworkOperation *op = operationInProgress();
       
  2227     if ( op ) {
       
  2228 	if ( err ) {
       
  2229 	    op->setProtocolDetail( errorString() );
       
  2230 	    op->setState( StFailed );
       
  2231 	    if ( error() == HostNotFound ) {
       
  2232 		op->setErrorCode( (int)ErrHostNotFound );
       
  2233 	    } else {
       
  2234 		switch ( op->operation() ) {
       
  2235 		    case OpListChildren:
       
  2236 			op->setErrorCode( (int)ErrListChildren );
       
  2237 			break;
       
  2238 		    case OpMkDir:
       
  2239 			op->setErrorCode( (int)ErrMkDir );
       
  2240 			break;
       
  2241 		    case OpRemove:
       
  2242 			op->setErrorCode( (int)ErrRemove );
       
  2243 			break;
       
  2244 		    case OpRename:
       
  2245 			op->setErrorCode( (int)ErrRename );
       
  2246 			break;
       
  2247 		    case OpGet:
       
  2248 			op->setErrorCode( (int)ErrGet );
       
  2249 			break;
       
  2250 		    case OpPut:
       
  2251 			op->setErrorCode( (int)ErrPut );
       
  2252 			break;
       
  2253 		}
       
  2254 	    }
       
  2255 	    emitFinishedSignal = true;
       
  2256 	} else if ( !d->npWaitForLoginDone ) {
       
  2257 	    switch ( op->operation() ) {
       
  2258 		case OpRemove:
       
  2259 		    emit removed( op );
       
  2260 		    break;
       
  2261 		case OpMkDir:
       
  2262 		    {
       
  2263 			QUrlInfo inf( op->arg( 0 ), 0, QLatin1String(""), QLatin1String(""), 0, QDateTime(),
       
  2264 				QDateTime(), true, false, false, true, true, true );
       
  2265 			emit newChild( inf, op );
       
  2266 			emit createdDirectory( inf, op );
       
  2267 		    }
       
  2268 		    break;
       
  2269 		case OpRename:
       
  2270 		    emit itemChanged( operationInProgress() );
       
  2271 		    break;
       
  2272 		default:
       
  2273 		    break;
       
  2274 	    }
       
  2275 	    op->setState( StDone );
       
  2276 	    emitFinishedSignal = true;
       
  2277 	}
       
  2278     }
       
  2279     d->npWaitForLoginDone = false;
       
  2280 
       
  2281     if ( state() == Unconnected ) {
       
  2282 	disconnect( this, SIGNAL(listInfo(QUrlInfo)),
       
  2283 		    this, SLOT(npListInfo(QUrlInfo)) );
       
  2284 	disconnect( this, SIGNAL(done(bool)),
       
  2285 		    this, SLOT(npDone(bool)) );
       
  2286 	disconnect( this, SIGNAL(stateChanged(int)),
       
  2287 		    this, SLOT(npStateChanged(int)) );
       
  2288 	disconnect( this, SIGNAL(dataTransferProgress(int,int)),
       
  2289 		    this, SLOT(npDataTransferProgress(int,int)) );
       
  2290 	disconnect( this, SIGNAL(readyRead()),
       
  2291 		    this, SLOT(npReadyRead()) );
       
  2292     }
       
  2293 
       
  2294     // emit the finished() signal at the very end to avoid reentrance problems
       
  2295     if ( emitFinishedSignal )
       
  2296 	emit finished( op );
       
  2297 }
       
  2298 
       
  2299 void Q3Ftp::npStateChanged( int state )
       
  2300 {
       
  2301     if ( url() ) {
       
  2302 	if ( state == Connecting )
       
  2303 	    emit connectionStateChanged( ConHostFound, QFtp::tr( "Host %1 found" ).arg( url()->host() ) );
       
  2304 	else if ( state == Connected )
       
  2305 	    emit connectionStateChanged( ConConnected, QFtp::tr( "Connected to host %1" ).arg( url()->host() ) );
       
  2306 	else if ( state == Unconnected )
       
  2307 	    emit connectionStateChanged( ConClosed, QFtp::tr( "Connection to %1 closed" ).arg( url()->host() ) );
       
  2308     } else {
       
  2309 	if ( state == Connecting )
       
  2310 	    emit connectionStateChanged( ConHostFound, QFtp::tr( "Host found" ) );
       
  2311 	else if ( state == Connected )
       
  2312 	    emit connectionStateChanged( ConConnected, QFtp::tr( "Connected to host" ) );
       
  2313 	else if ( state == Unconnected )
       
  2314 	    emit connectionStateChanged( ConClosed, QFtp::tr( "Connection closed" ) );
       
  2315     }
       
  2316 }
       
  2317 
       
  2318 void Q3Ftp::npDataTransferProgress( int bDone, int bTotal )
       
  2319 {
       
  2320     emit Q3NetworkProtocol::dataTransferProgress( bDone, bTotal, operationInProgress() );
       
  2321 }
       
  2322 
       
  2323 void Q3Ftp::npReadyRead()
       
  2324 {
       
  2325     emit data( readAll(), operationInProgress() );
       
  2326 }
       
  2327 
       
  2328 /*!  \internal
       
  2329 */
       
  2330 void Q3Ftp::hostFound()
       
  2331 {
       
  2332 }
       
  2333 /*!  \internal
       
  2334 */
       
  2335 void Q3Ftp::connected()
       
  2336 {
       
  2337 }
       
  2338 /*!  \internal
       
  2339 */
       
  2340 void Q3Ftp::closed()
       
  2341 {
       
  2342 }
       
  2343 /*!  \internal
       
  2344 */
       
  2345 void Q3Ftp::dataHostFound()
       
  2346 {
       
  2347 }
       
  2348 /*!  \internal
       
  2349 */
       
  2350 void Q3Ftp::dataConnected()
       
  2351 {
       
  2352 }
       
  2353 /*!  \internal
       
  2354 */
       
  2355 void Q3Ftp::dataClosed()
       
  2356 {
       
  2357 }
       
  2358 /*!  \internal
       
  2359 */
       
  2360 void Q3Ftp::dataReadyRead()
       
  2361 {
       
  2362 }
       
  2363 /*!  \internal
       
  2364 */
       
  2365 void Q3Ftp::dataBytesWritten( int )
       
  2366 {
       
  2367 }
       
  2368 /*!  \internal
       
  2369 */
       
  2370 void Q3Ftp::error( int )
       
  2371 {
       
  2372 }
       
  2373 
       
  2374 QT_END_NAMESPACE
       
  2375 
       
  2376 #include "q3ftp.moc"
       
  2377 
       
  2378 #endif // QT_NO_NETWORKPROTOCOL_FTP