source: trunk/src/qt3support/network/q3ftp.cpp@ 158

Last change on this file since 158 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 66.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the Qt3Support module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
64QT_BEGIN_NAMESPACE
65
66//#define Q3FTPPI_DEBUG
67//#define Q3FTPDTP_DEBUG
68
69class Q3FtpPI;
70
71class Q3FtpDTP : public QObject
72{
73 Q_OBJECT
74
75public:
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
128signals:
129 void listInfo( const QUrlInfo& );
130 void readyRead();
131 void dataTransferProgress( int, int );
132
133 void connectState( int );
134
135private slots:
136 void socketConnected();
137 void socketReadyRead();
138 void socketError( int );
139 void socketConnectionClosed();
140 void socketBytesWritten( int );
141
142private:
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
165class Q3FtpPI : public QObject
166{
167 Q_OBJECT
168
169public:
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
188signals:
189 void connectState( int );
190 void finished( const QString& );
191 void error( int, const QString& );
192 void rawFtpReply( int, const QString& );
193
194private 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
204private:
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 *********************************************************************/
241class Q3FtpCommand
242{
243public:
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
264int Q3FtpCommand::idCounter = 0;
265
266Q3FtpCommand::Q3FtpCommand( Q3Ftp::Command cmd, QStringList raw )
267 : command(cmd), rawCmds(raw), is_ba(false)
268{
269 id = ++idCounter;
270 data.dev = 0;
271}
272
273Q3FtpCommand::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
280Q3FtpCommand::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
287Q3FtpCommand::~Q3FtpCommand()
288{
289 if ( is_ba )
290 delete data.ba;
291}
292
293/**********************************************************************
294 *
295 * Q3FtpDTP implemenatation
296 *
297 *********************************************************************/
298Q3FtpDTP::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
318void Q3FtpDTP::setData( QByteArray *ba )
319{
320 is_ba = true;
321 data.ba = ba;
322}
323
324void Q3FtpDTP::setDevice( QIODevice *dev )
325{
326 is_ba = false;
327 data.dev = dev;
328}
329
330void 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
366inline bool Q3FtpDTP::hasError() const
367{
368 return !err.isNull();
369}
370
371inline QString Q3FtpDTP::errorMessage() const
372{
373 return err;
374}
375
376inline void Q3FtpDTP::clearError()
377{
378 err.clear();
379}
380
381void 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
393bool 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( QLatin1String(":") ) ) {
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( QLatin1String(":") ) ) {
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 ] + QLatin1String(" ");
516 n = n.stripWhiteSpace();
517 info->setName( n );
518 }
519 return true;
520}
521
522void 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
540void 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
594void 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
609void 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
620void 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 *********************************************************************/
636Q3FtpPI::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
663void 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*/
677bool 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
692void Q3FtpPI::clearPendingCommands()
693{
694 pendingCommands.clear();
695 dtp.abortConnection();
696 currentCmd.clear();
697 state = Idle;
698}
699
700void 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
718void Q3FtpPI::hostFound()
719{
720 emit connectState( Q3Ftp::Connecting );
721}
722
723void 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
732void Q3FtpPI::connectionClosed()
733{
734 commandSocket.close();
735 emit connectState( Q3Ftp::Unconnected );
736}
737
738void Q3FtpPI::delayedCloseFinished()
739{
740 emit connectState( Q3Ftp::Unconnected );
741}
742
743void 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
756void 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*/
813bool 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] + QLatin1String(".") + lst[2] + QLatin1String(".") + lst[3] + QLatin1String(".") + 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
953Q_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*/
960bool 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
994void 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 *********************************************************************/
1028class Q3FtpPrivate
1029{
1030public:
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
1048static Q3PtrDict<Q3FtpPrivate> *d_ptr = 0;
1049static void cleanup_d_ptr()
1050{
1051 delete d_ptr;
1052 d_ptr = 0;
1053}
1054static 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
1069static 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 Trolltech's
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*/
1190Q3Ftp::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*/
1199Q3Ftp::Q3Ftp( QObject *parent, const char *name ) : Q3NetworkProtocol()
1200{
1201 if ( parent )
1202 parent->insertChild( this );
1203 setName( name );
1204 init();
1205}
1206
1207void 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*/
1409int 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*/
1435int Q3Ftp::login( const QString &user, const QString &password )
1436{
1437 QStringList cmds;
1438 cmds << ( QString(QLatin1String("USER ")) + ( user.isNull() ? QString(QLatin1String("anonymous")) : user ) + QLatin1String("\r\n") );
1439 cmds << ( QString(QLatin1String("PASS ")) + ( password.isNull() ? QString(QLatin1String("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*/
1461int 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*/
1483int 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*/
1509int 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*/
1551int 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*/
1581int 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*/
1602int 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*/
1627int 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*/
1646int 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*/
1665int 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*/
1684int 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*/
1710int 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*/
1722Q_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*/
1734Q_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*/
1746QByteArray 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*/
1781void 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*/
1797int 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*/
1812Q3Ftp::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*/
1831QIODevice* 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*/
1851bool 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*/
1864void 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*/
1881Q3Ftp::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*/
1894Q3Ftp::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*/
1911QString Q3Ftp::errorString() const
1912{
1913 Q3FtpPrivate *d = dHelper( this );
1914 return d->errorString;
1915}
1916
1917int 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
1929void 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
1972void 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
1999void 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
2057void 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
2068void 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*/
2080Q3Ftp::~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*/
2094void Q3Ftp::operationListChildren( Q3NetworkOperation *op )
2095{
2096 op->setState( StInProgress );
2097
2098 cd( ( url()->path().isEmpty() ? QString( QLatin1String("/") ) : url()->path() ) );
2099 list();
2100 emit start( op );
2101}
2102
2103/*! \reimp
2104*/
2105void Q3Ftp::operationMkDir( Q3NetworkOperation *op )
2106{
2107 op->setState( StInProgress );
2108
2109 mkdir( op->arg( 0 ) );
2110}
2111
2112/*! \reimp
2113*/
2114void Q3Ftp::operationRemove( Q3NetworkOperation *op )
2115{
2116 op->setState( StInProgress );
2117
2118 cd( ( url()->path().isEmpty() ? QString( QLatin1String("/") ) : url()->path() ) );
2119 remove( Q3Url( op->arg( 0 ) ).path() );
2120}
2121
2122/*! \reimp
2123*/
2124void Q3Ftp::operationRename( Q3NetworkOperation *op )
2125{
2126 op->setState( StInProgress );
2127
2128 cd( ( url()->path().isEmpty() ? QString( QLatin1String("/") ) : url()->path() ) );
2129 rename( op->arg( 0 ), op->arg( 1 ));
2130}
2131
2132/*! \reimp
2133*/
2134void 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*/
2144void 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*/
2154bool 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( QLatin1String("anonymous") ) : url()->user();
2183 QString pass = url()->password().isEmpty() ? QString( QLatin1String("anonymous@") ) : url()->password();
2184 login( user, pass );
2185 }
2186
2187 if ( state() == LoggedIn )
2188 return true;
2189 return false;
2190}
2191
2192/*! \reimp
2193*/
2194int 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*/
2204void Q3Ftp::parseDir( const QString &buffer, QUrlInfo &info )
2205{
2206 Q3FtpDTP::parseDir( buffer, url()->user(), &info );
2207}
2208
2209void 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
2221void 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
2299void 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
2318void Q3Ftp::npDataTransferProgress( int bDone, int bTotal )
2319{
2320 emit Q3NetworkProtocol::dataTransferProgress( bDone, bTotal, operationInProgress() );
2321}
2322
2323void Q3Ftp::npReadyRead()
2324{
2325 emit data( readAll(), operationInProgress() );
2326}
2327
2328/*! \internal
2329*/
2330void Q3Ftp::hostFound()
2331{
2332}
2333/*! \internal
2334*/
2335void Q3Ftp::connected()
2336{
2337}
2338/*! \internal
2339*/
2340void Q3Ftp::closed()
2341{
2342}
2343/*! \internal
2344*/
2345void Q3Ftp::dataHostFound()
2346{
2347}
2348/*! \internal
2349*/
2350void Q3Ftp::dataConnected()
2351{
2352}
2353/*! \internal
2354*/
2355void Q3Ftp::dataClosed()
2356{
2357}
2358/*! \internal
2359*/
2360void Q3Ftp::dataReadyRead()
2361{
2362}
2363/*! \internal
2364*/
2365void Q3Ftp::dataBytesWritten( int )
2366{
2367}
2368/*! \internal
2369*/
2370void Q3Ftp::error( int )
2371{
2372}
2373
2374QT_END_NAMESPACE
2375
2376#include "q3ftp.moc"
2377
2378#endif // QT_NO_NETWORKPROTOCOL_FTP
Note: See TracBrowser for help on using the repository browser.