source: trunk/src/network/qftp.cpp@ 7

Last change on this file since 7 was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

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