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