1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 //#define QFTPPI_DEBUG
43 //#define QFTPDTP_DEBUG
44 
45 #include "qftp.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);
sendCommand(const QString & cmd)162     bool sendCommand(const QString &cmd)
163         { return sendCommands(QStringList(cmd)); }
164 
165     void clearPendingCommands();
166     void abort();
167 
currentCommand() const168     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 
QFtpCommand(QFtp::Command cmd,QStringList raw,const QByteArray & ba)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 
QFtpCommand(QFtp::Command cmd,QStringList raw,QIODevice * dev)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 
~QFtpCommand()271 QFtpCommand::~QFtpCommand()
272 {
273     if (is_ba)
274         delete data.ba;
275 }
276 
277 /**********************************************************************
278  *
279  * QFtpDTP implemenatation
280  *
281  *********************************************************************/
QFtpDTP(QFtpPI * p,QObject * parent)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 
setData(QByteArray * ba)294 void QFtpDTP::setData(QByteArray *ba)
295 {
296     is_ba = true;
297     data.ba = ba;
298 }
299 
setDevice(QIODevice * dev)300 void QFtpDTP::setDevice(QIODevice *dev)
301 {
302     is_ba = false;
303     data.dev = dev;
304 }
305 
setBytesTotal(qint64 bytes)306 void QFtpDTP::setBytesTotal(qint64 bytes)
307 {
308     bytesTotal = bytes;
309     bytesDone = 0;
310     emit dataTransferProgress(bytesDone, bytesTotal);
311 }
312 
connectToHost(const QString & host,quint16 port)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 
setupListener(const QHostAddress & address)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 
waitForConnection()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 
state() const356 QTcpSocket::SocketState QFtpDTP::state() const
357 {
358     return socket ? socket->state() : QTcpSocket::UnconnectedState;
359 }
360 
bytesAvailable() const361 qint64 QFtpDTP::bytesAvailable() const
362 {
363     if (!socket || socket->state() != QTcpSocket::ConnectedState)
364         return (qint64) bytesFromSocket.size();
365     return socket->bytesAvailable();
366 }
367 
read(char * data,qint64 maxlen)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 
readAll()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 
writeData()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 
dataReadyRead()436 void QFtpDTP::dataReadyRead()
437 {
438     writeData();
439 }
440 
hasError() const441 inline bool QFtpDTP::hasError() const
442 {
443     return !err.isNull();
444 }
445 
errorMessage() const446 inline QString QFtpDTP::errorMessage() const
447 {
448     return err;
449 }
450 
clearError()451 inline void QFtpDTP::clearError()
452 {
453     err.clear();
454 }
455 
abortConnection()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 
_q_fixupDateTime(QDateTime * dateTime)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 
_q_parseUnixDir(const QStringList & tokens,const QString & userName,QUrlInfo * info)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 
_q_parseDosDir(const QStringList & tokens,const QString & userName,QUrlInfo * info)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 
parseDir(const QByteArray & buffer,const QString & userName,QUrlInfo * info)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:\\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 
socketConnected()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 
socketReadyRead()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::AbortStarted) {
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 
socketError(QAbstractSocket::SocketError e)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 
socketConnectionClosed()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 
socketBytesWritten(qint64 bytes)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 
setupSocket()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 
clearData()778 void QFtpDTP::clearData()
779 {
780     is_ba = false;
781     data.dev = 0;
782 }
783 
784 /**********************************************************************
785  *
786  * QFtpPI implemenatation
787  *
788  *********************************************************************/
QFtpPI(QObject * parent)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 
connectToHost(const QString & host,quint16 port)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   Sends the sequence of commands \a cmds to the FTP server. When the commands
829   are all done the finished() signal is emitted. When an error occurs, the
830   error() signal is emitted.
831 
832   If there are pending commands in the queue this functions returns false and
833   the \a cmds are not added to the queue; otherwise it returns true.
834 */
sendCommands(const QStringList & cmds)835 bool QFtpPI::sendCommands(const QStringList &cmds)
836 {
837     if (!pendingCommands.isEmpty())
838         return false;
839 
840     if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {
841         emit error(QFtp::NotConnected, QFtp::tr("Not connected"));
842         return true; // there are no pending commands
843     }
844 
845     pendingCommands = cmds;
846     startNextCmd();
847     return true;
848 }
849 
clearPendingCommands()850 void QFtpPI::clearPendingCommands()
851 {
852     pendingCommands.clear();
853     dtp.abortConnection();
854     currentCmd.clear();
855     state = Idle;
856 }
857 
abort()858 void QFtpPI::abort()
859 {
860     pendingCommands.clear();
861 
862     if (abortState != None)
863         // ABOR already sent
864         return;
865 
866     abortState = AbortStarted;
867 #if defined(QFTPPI_DEBUG)
868     qDebug("QFtpPI send: ABOR");
869 #endif
870     commandSocket.write("ABOR\r\n", 6);
871 
872     if (currentCmd.startsWith(QLatin1String("STOR ")))
873         dtp.abortConnection();
874 }
875 
hostFound()876 void QFtpPI::hostFound()
877 {
878     emit connectState(QFtp::Connecting);
879 }
880 
connected()881 void QFtpPI::connected()
882 {
883     state = Begin;
884 #if defined(QFTPPI_DEBUG)
885 //    qDebug("QFtpPI state: %d [connected()]", state);
886 #endif
887     // try to improve performance by setting TCP_NODELAY
888     commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
889 
890     emit connectState(QFtp::Connected);
891 }
892 
connectionClosed()893 void QFtpPI::connectionClosed()
894 {
895     commandSocket.close();
896     emit connectState(QFtp::Unconnected);
897 }
898 
delayedCloseFinished()899 void QFtpPI::delayedCloseFinished()
900 {
901     emit connectState(QFtp::Unconnected);
902 }
903 
error(QAbstractSocket::SocketError e)904 void QFtpPI::error(QAbstractSocket::SocketError e)
905 {
906     if (e == QTcpSocket::HostNotFoundError) {
907         emit connectState(QFtp::Unconnected);
908         emit error(QFtp::HostNotFound,
909                     QFtp::tr("Host %1 not found").arg(commandSocket.peerName()));
910     } else if (e == QTcpSocket::ConnectionRefusedError) {
911         emit connectState(QFtp::Unconnected);
912         emit error(QFtp::ConnectionRefused,
913                     QFtp::tr("Connection refused to host %1").arg(commandSocket.peerName()));
914     } else if (e == QTcpSocket::SocketTimeoutError) {
915         emit connectState(QFtp::Unconnected);
916         emit error(QFtp::ConnectionRefused,
917                    QFtp::tr("Connection timed out to host %1").arg(commandSocket.peerName()));
918     }
919 }
920 
readyRead()921 void QFtpPI::readyRead()
922 {
923     if (waitForDtpToClose)
924         return;
925 
926     while (commandSocket.canReadLine()) {
927         // read line with respect to line continuation
928         QString line = QString::fromLatin1(commandSocket.readLine());
929         if (replyText.isEmpty()) {
930             if (line.length() < 3) {
931                 // protocol error
932                 return;
933             }
934             const int lowerLimit[3] = {1,0,0};
935             const int upperLimit[3] = {5,5,9};
936             for (int i=0; i<3; i++) {
937                 replyCode[i] = line[i].digitValue();
938                 if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {
939                     // protocol error
940                     return;
941                 }
942             }
943         }
944         QString endOfMultiLine;
945         endOfMultiLine[0] = '0' + replyCode[0];
946         endOfMultiLine[1] = '0' + replyCode[1];
947         endOfMultiLine[2] = '0' + replyCode[2];
948         endOfMultiLine[3] = QLatin1Char(' ');
949         QString lineCont(endOfMultiLine);
950         lineCont[3] = QLatin1Char('-');
951         QString lineLeft4 = line.left(4);
952 
953         while (lineLeft4 != endOfMultiLine) {
954             if (lineLeft4 == lineCont)
955                 replyText += line.mid(4); // strip 'xyz-'
956             else
957                 replyText += line;
958             if (!commandSocket.canReadLine())
959                 return;
960             line = QString::fromLatin1(commandSocket.readLine());
961             lineLeft4 = line.left(4);
962         }
963         replyText += line.mid(4); // strip reply code 'xyz '
964         if (replyText.endsWith(QLatin1String("\r\n")))
965             replyText.chop(2);
966 
967         if (processReply())
968             replyText = QLatin1String("");
969     }
970 }
971 
972 /*
973   Process a reply from the FTP server.
974 
975   Returns true if the reply was processed or false if the reply has to be
976   processed at a later point.
977 */
processReply()978 bool QFtpPI::processReply()
979 {
980 #if defined(QFTPPI_DEBUG)
981 //    qDebug("QFtpPI state: %d [processReply() begin]", state);
982     if (replyText.length() < 400)
983         qDebug("QFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());
984     else
985         qDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]);
986 #endif
987 
988     int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
989 
990     // process 226 replies ("Closing Data Connection") only when the data
991     // connection is really closed to avoid short reads of the DTP
992     if (replyCodeInt == 226 || (replyCodeInt == 250 && currentCmd.startsWith(QLatin1String("RETR")))) {
993         if (dtp.state() != QTcpSocket::UnconnectedState) {
994             waitForDtpToClose = true;
995             return false;
996         }
997     }
998 
999     switch (abortState) {
1000         case AbortStarted:
1001             abortState = WaitForAbortToFinish;
1002             break;
1003         case WaitForAbortToFinish:
1004             abortState = None;
1005             return true;
1006         default:
1007             break;
1008     }
1009 
1010     // get new state
1011     static const State table[5] = {
1012         /* 1yz   2yz      3yz   4yz      5yz */
1013         Waiting, Success, Idle, Failure, Failure
1014     };
1015     switch (state) {
1016         case Begin:
1017             if (replyCode[0] == 1) {
1018                 return true;
1019             } else if (replyCode[0] == 2) {
1020                 state = Idle;
1021                 emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));
1022                 break;
1023             }
1024             // reply codes not starting with 1 or 2 are not handled.
1025             return true;
1026         case Waiting:
1027             if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)
1028                 state = Failure;
1029             else
1030 #if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
1031             {
1032                 // work around a crash on 64 bit gcc IRIX
1033                 State *t = (State *) table;
1034                 state = t[replyCode[0] - 1];
1035             }
1036 #else
1037             if (replyCodeInt == 202)
1038                 state = Failure;
1039             else
1040                 state = table[replyCode[0] - 1];
1041 #endif
1042             break;
1043         default:
1044             // ignore unrequested message
1045             return true;
1046     }
1047 #if defined(QFTPPI_DEBUG)
1048 //    qDebug("QFtpPI state: %d [processReply() intermediate]", state);
1049 #endif
1050 
1051     // special actions on certain replies
1052     emit rawFtpReply(replyCodeInt, replyText);
1053     if (rawCommand) {
1054         rawCommand = false;
1055     } else if (replyCodeInt == 227) {
1056         // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
1057         // rfc959 does not define this response precisely, and gives
1058         // both examples where the parenthesis are used, and where
1059         // they are missing. We need to scan for the address and host
1060         // info.
1061         QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));
1062         if (addrPortPattern.indexIn(replyText) == -1) {
1063 #if defined(QFTPPI_DEBUG)
1064             qDebug("QFtp: bad 227 response -- address and port information missing");
1065 #endif
1066             // this error should be reported
1067         } else {
1068             QStringList lst = addrPortPattern.capturedTexts();
1069             QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];
1070             quint16 port = (lst[5].toUInt() << 8) + lst[6].toUInt();
1071             waitForDtpToConnect = true;
1072             dtp.connectToHost(host, port);
1073         }
1074     } else if (replyCodeInt == 229) {
1075         // 229 Extended Passive mode OK (|||10982|)
1076         int portPos = replyText.indexOf(QLatin1Char('('));
1077         if (portPos == -1) {
1078 #if defined(QFTPPI_DEBUG)
1079             qDebug("QFtp: bad 229 response -- port information missing");
1080 #endif
1081             // this error should be reported
1082         } else {
1083             ++portPos;
1084             QChar delimiter = replyText.at(portPos);
1085             QStringList epsvParameters = replyText.mid(portPos).split(delimiter);
1086 
1087             waitForDtpToConnect = true;
1088             dtp.connectToHost(commandSocket.peerAddress().toString(),
1089                               epsvParameters.at(3).toInt());
1090         }
1091 
1092     } else if (replyCodeInt == 230) {
1093         if (currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&
1094             pendingCommands.first().startsWith(QLatin1String("PASS "))) {
1095             // no need to send the PASS -- we are already logged in
1096             pendingCommands.pop_front();
1097         }
1098         // 230 User logged in, proceed.
1099         emit connectState(QFtp::LoggedIn);
1100     } else if (replyCodeInt == 213) {
1101         // 213 File status.
1102         if (currentCmd.startsWith(QLatin1String("SIZE ")))
1103             dtp.setBytesTotal(replyText.simplified().toLongLong());
1104     } else if (replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR "))) {
1105         dtp.waitForConnection();
1106         dtp.writeData();
1107     }
1108 
1109     // react on new state
1110     switch (state) {
1111         case Begin:
1112             // should never happen
1113             break;
1114         case Success:
1115             // success handling
1116             state = Idle;
1117             // no break!
1118         case Idle:
1119             if (dtp.hasError()) {
1120                 emit error(QFtp::UnknownError, dtp.errorMessage());
1121                 dtp.clearError();
1122             }
1123             startNextCmd();
1124             break;
1125         case Waiting:
1126             // do nothing
1127             break;
1128         case Failure:
1129             // If the EPSV or EPRT commands fail, replace them with
1130             // the old PASV and PORT instead and try again.
1131             if (currentCmd.startsWith(QLatin1String("EPSV"))) {
1132                 transferConnectionExtended = false;
1133                 pendingCommands.prepend(QLatin1String("PASV\r\n"));
1134             } else if (currentCmd.startsWith(QLatin1String("EPRT"))) {
1135                 transferConnectionExtended = false;
1136                 pendingCommands.prepend(QLatin1String("PORT\r\n"));
1137             } else {
1138                 emit error(QFtp::UnknownError, replyText);
1139             }
1140             if (state != Waiting) {
1141                 state = Idle;
1142                 startNextCmd();
1143             }
1144             break;
1145     }
1146 #if defined(QFTPPI_DEBUG)
1147 //    qDebug("QFtpPI state: %d [processReply() end]", state);
1148 #endif
1149     return true;
1150 }
1151 
1152 /*
1153   Starts next pending command. Returns false if there are no pending commands,
1154   otherwise it returns true.
1155 */
startNextCmd()1156 bool QFtpPI::startNextCmd()
1157 {
1158     if (waitForDtpToConnect)
1159         // don't process any new commands until we are connected
1160         return true;
1161 
1162 #if defined(QFTPPI_DEBUG)
1163     if (state != Idle)
1164         qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state);
1165 #endif
1166     if (pendingCommands.isEmpty()) {
1167         currentCmd.clear();
1168         emit finished(replyText);
1169         return false;
1170     }
1171     currentCmd = pendingCommands.first();
1172 
1173     // PORT and PASV are edited in-place, depending on whether we
1174     // should try the extended transfer connection commands EPRT and
1175     // EPSV. The PORT command also triggers setting up a listener, and
1176     // the address/port arguments are edited in.
1177     QHostAddress address = commandSocket.localAddress();
1178     if (currentCmd.startsWith(QLatin1String("PORT"))) {
1179         if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended) {
1180             int port = dtp.setupListener(address);
1181             currentCmd = QLatin1String("EPRT |");
1182             currentCmd += (address.protocol() == QTcpSocket::IPv4Protocol) ? QLatin1Char('1') : QLatin1Char('2');
1183             currentCmd += QLatin1Char('|') + address.toString() + QLatin1Char('|') + QString::number(port);
1184             currentCmd += QLatin1Char('|');
1185         } else if (address.protocol() == QTcpSocket::IPv4Protocol) {
1186             int port = dtp.setupListener(address);
1187             QString portArg;
1188             quint32 ip = address.toIPv4Address();
1189             portArg += QString::number((ip & 0xff000000) >> 24);
1190             portArg += QLatin1Char(',') + QString::number((ip & 0xff0000) >> 16);
1191             portArg += QLatin1Char(',') + QString::number((ip & 0xff00) >> 8);
1192             portArg += QLatin1Char(',') + QString::number(ip & 0xff);
1193             portArg += QLatin1Char(',') + QString::number((port & 0xff00) >> 8);
1194             portArg += QLatin1Char(',') + QString::number(port & 0xff);
1195 
1196             currentCmd = QLatin1String("PORT ");
1197             currentCmd += portArg;
1198         } else {
1199             // No IPv6 connection can be set up with the PORT
1200             // command.
1201             return false;
1202         }
1203 
1204         currentCmd += QLatin1String("\r\n");
1205     } else if (currentCmd.startsWith(QLatin1String("PASV"))) {
1206         if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended)
1207             currentCmd = QLatin1String("EPSV\r\n");
1208     }
1209 
1210     pendingCommands.pop_front();
1211 #if defined(QFTPPI_DEBUG)
1212     qDebug("QFtpPI send: %s", currentCmd.left(currentCmd.length()-2).toLatin1().constData());
1213 #endif
1214     state = Waiting;
1215     commandSocket.write(currentCmd.toLatin1());
1216     return true;
1217 }
1218 
dtpConnectState(int s)1219 void QFtpPI::dtpConnectState(int s)
1220 {
1221     switch (s) {
1222         case QFtpDTP::CsClosed:
1223             if (waitForDtpToClose) {
1224                 // there is an unprocessed reply
1225                 if (processReply())
1226                     replyText = QLatin1String("");
1227                 else
1228                     return;
1229             }
1230             waitForDtpToClose = false;
1231             readyRead();
1232             return;
1233         case QFtpDTP::CsConnected:
1234             waitForDtpToConnect = false;
1235             startNextCmd();
1236             return;
1237         case QFtpDTP::CsHostNotFound:
1238         case QFtpDTP::CsConnectionRefused:
1239             emit error(QFtp::ConnectionRefused,
1240                         QFtp::tr("Connection refused for data connection"));
1241             startNextCmd();
1242             return;
1243         default:
1244             return;
1245     }
1246 }
1247 
1248 /**********************************************************************
1249  *
1250  * QFtpPrivate
1251  *
1252  *********************************************************************/
1253 
1254 class QFtpPrivate
1255 {
1256     Q_DECLARE_PUBLIC(QFtp)
1257 public:
1258 
QFtpPrivate(QFtp * owner)1259     inline QFtpPrivate(QFtp *owner) : close_waitForStateChange(false), state(QFtp::Unconnected),
1260         transferMode(QFtp::Passive), error(QFtp::NoError), q_ptr(owner)
1261     { }
1262 
~QFtpPrivate()1263     ~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }
1264 
1265     // private slots
1266     void _q_startNextCommand();
1267     void _q_piFinished(const QString&);
1268     void _q_piError(int, const QString&);
1269     void _q_piConnectState(int);
1270     void _q_piFtpReply(int, const QString&);
1271 
1272     int addCommand(QFtpCommand *cmd);
1273 
1274     QFtpPI pi;
1275     QList<QFtpCommand *> pending;
1276     bool close_waitForStateChange;
1277     QFtp::State state;
1278     QFtp::TransferMode transferMode;
1279     QFtp::Error error;
1280     QString errorString;
1281 
1282     QString host;
1283     quint16 port;
1284     QString proxyHost;
1285     quint16 proxyPort;
1286     QFtp *q_ptr;
1287 };
1288 
addCommand(QFtpCommand * cmd)1289 int QFtpPrivate::addCommand(QFtpCommand *cmd)
1290 {
1291     pending.append(cmd);
1292 
1293     if (pending.count() == 1) {
1294         // don't emit the commandStarted() signal before the ID is returned
1295         QTimer::singleShot(0, q_func(), SLOT(_q_startNextCommand()));
1296     }
1297     return cmd->id;
1298 }
1299 
1300 /**********************************************************************
1301  *
1302  * QFtp implementation
1303  *
1304  *********************************************************************/
1305 /*!
1306     \class QFtp
1307     \brief The QFtp class provides an implementation of the client side of FTP protocol.
1308 
1309     \ingroup network
1310     \inmodule QtNetwork
1311 
1312 
1313     This class provides a direct interface to FTP that allows you to
1314     have more control over the requests. However, for new
1315     applications, it is recommended to use QNetworkAccessManager and
1316     QNetworkReply, as those classes possess a simpler, yet more
1317     powerful API.
1318 
1319     The class works asynchronously, so there are no blocking
1320     functions. If an operation cannot be executed immediately, the
1321     function will still return straight away and the operation will be
1322     scheduled for later execution. The results of scheduled operations
1323     are reported via signals. This approach depends on the event loop
1324     being in operation.
1325 
1326     The operations that can be scheduled (they are called "commands"
1327     in the rest of the documentation) are the following:
1328     connectToHost(), login(), close(), list(), cd(), get(), put(),
1329     remove(), mkdir(), rmdir(), rename() and rawCommand().
1330 
1331     All of these commands return a unique identifier that allows you
1332     to keep track of the command that is currently being executed.
1333     When the execution of a command starts, the commandStarted()
1334     signal with the command's identifier is emitted. When the command
1335     is finished, the commandFinished() signal is emitted with the
1336     command's identifier and a bool that indicates whether the command
1337     finished with an error.
1338 
1339     In some cases, you might want to execute a sequence of commands,
1340     e.g. if you want to connect and login to a FTP server. This is
1341     simply achieved:
1342 
1343     \snippet doc/src/snippets/code/src_network_access_qftp.cpp 0
1344 
1345     In this case two FTP commands have been scheduled. When the last
1346     scheduled command has finished, a done() signal is emitted with
1347     a bool argument that tells you whether the sequence finished with
1348     an error.
1349 
1350     If an error occurs during the execution of one of the commands in
1351     a sequence of commands, all the pending commands (i.e. scheduled,
1352     but not yet executed commands) are cleared and no signals are
1353     emitted for them.
1354 
1355     Some commands, e.g. list(), emit additional signals to report
1356     their results.
1357 
1358     Example: If you want to download the INSTALL file from the Qt
1359     FTP server, you would write this:
1360 
1361     \snippet doc/src/snippets/code/src_network_access_qftp.cpp 1
1362 
1363     For this example the following sequence of signals is emitted
1364     (with small variations, depending on network traffic, etc.):
1365 
1366     \snippet doc/src/snippets/code/src_network_access_qftp.cpp 2
1367 
1368     The dataTransferProgress() signal in the above example is useful
1369     if you want to show a \link QProgressBar progress bar \endlink to
1370     inform the user about the progress of the download. The
1371     readyRead() signal tells you that there is data ready to be read.
1372     The amount of data can be queried then with the bytesAvailable()
1373     function and it can be read with the read() or readAll()
1374     function.
1375 
1376     If the login fails for the above example, the signals would look
1377     like this:
1378 
1379     \snippet doc/src/snippets/code/src_network_access_qftp.cpp 3
1380 
1381     You can then get details about the error with the error() and
1382     errorString() functions.
1383 
1384     For file transfer, QFtp can use both active or passive mode, and
1385     it uses passive file transfer mode by default; see the
1386     documentation for setTransferMode() for more details about this.
1387 
1388     Call setProxy() to make QFtp connect via an FTP proxy server.
1389 
1390     The functions currentId() and currentCommand() provide more
1391     information about the currently executing command.
1392 
1393     The functions hasPendingCommands() and clearPendingCommands()
1394     allow you to query and clear the list of pending commands.
1395 
1396     If you are an experienced network programmer and want to have
1397     complete control you can use rawCommand() to execute arbitrary FTP
1398     commands.
1399 
1400     \warning The current version of QFtp doesn't fully support
1401     non-Unix FTP servers.
1402 
1403     \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
1404         {FTP Example}
1405 */
1406 
1407 
1408 /*!
1409     Constructs a QFtp object with the given \a parent.
1410 */
QFtp(QObject * parent)1411 QFtp::QFtp(QObject *parent)
1412     : QObject(parent), d(new QFtpPrivate(this))
1413 {
1414     d->errorString = tr("Unknown error");
1415 
1416     connect(&d->pi, SIGNAL(connectState(int)),
1417             SLOT(_q_piConnectState(int)));
1418     connect(&d->pi, SIGNAL(finished(QString)),
1419             SLOT(_q_piFinished(QString)));
1420     connect(&d->pi, SIGNAL(error(int,QString)),
1421             SLOT(_q_piError(int,QString)));
1422     connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),
1423             SLOT(_q_piFtpReply(int,QString)));
1424 
1425     connect(&d->pi.dtp, SIGNAL(readyRead()),
1426             SIGNAL(readyRead()));
1427     connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),
1428             SIGNAL(dataTransferProgress(qint64,qint64)));
1429     connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),
1430             SIGNAL(listInfo(QUrlInfo)));
1431 }
1432 
1433 /*!
1434     \enum QFtp::State
1435 
1436     This enum defines the connection state:
1437 
1438     \value Unconnected There is no connection to the host.
1439     \value HostLookup A host name lookup is in progress.
1440     \value Connecting An attempt to connect to the host is in progress.
1441     \value Connected Connection to the host has been achieved.
1442     \value LoggedIn Connection and user login have been achieved.
1443     \value Closing The connection is closing down, but it is not yet
1444     closed. (The state will be \c Unconnected when the connection is
1445     closed.)
1446 
1447     \sa stateChanged() state()
1448 */
1449 /*!
1450     \enum QFtp::TransferMode
1451 
1452     FTP works with two socket connections; one for commands and
1453     another for transmitting data. While the command connection is
1454     always initiated by the client, the second connection can be
1455     initiated by either the client or the server.
1456 
1457     This enum defines whether the client (Passive mode) or the server
1458     (Active mode) should set up the data connection.
1459 
1460     \value Passive The client connects to the server to transmit its
1461     data.
1462 
1463     \value Active The server connects to the client to transmit its
1464     data.
1465 */
1466 /*!
1467     \enum QFtp::TransferType
1468 
1469     This enum identifies the data transfer type used with get and
1470     put commands.
1471 
1472     \value Binary The data will be transferred in Binary mode.
1473 
1474     \value Ascii The data will be transferred in Ascii mode and new line
1475     characters will be converted to the local format.
1476 */
1477 /*!
1478     \enum QFtp::Error
1479 
1480     This enum identifies the error that occurred.
1481 
1482     \value NoError No error occurred.
1483     \value HostNotFound The host name lookup failed.
1484     \value ConnectionRefused The server refused the connection.
1485     \value NotConnected Tried to send a command, but there is no connection to
1486     a server.
1487     \value UnknownError An error other than those specified above
1488     occurred.
1489 
1490     \sa error()
1491 */
1492 
1493 /*!
1494     \enum QFtp::Command
1495 
1496     This enum is used as the return value for the currentCommand() function.
1497     This allows you to perform specific actions for particular
1498     commands, e.g. in a FTP client, you might want to clear the
1499     directory view when a list() command is started; in this case you
1500     can simply check in the slot connected to the start() signal if
1501     the currentCommand() is \c List.
1502 
1503     \value None No command is being executed.
1504     \value SetTransferMode set the \link TransferMode transfer\endlink mode.
1505     \value SetProxy switch proxying on or off.
1506     \value ConnectToHost connectToHost() is being executed.
1507     \value Login login() is being executed.
1508     \value Close close() is being executed.
1509     \value List list() is being executed.
1510     \value Cd cd() is being executed.
1511     \value Get get() is being executed.
1512     \value Put put() is being executed.
1513     \value Remove remove() is being executed.
1514     \value Mkdir mkdir() is being executed.
1515     \value Rmdir rmdir() is being executed.
1516     \value Rename rename() is being executed.
1517     \value RawCommand rawCommand() is being executed.
1518 
1519     \sa currentCommand()
1520 */
1521 
1522 /*!
1523     \fn void QFtp::stateChanged(int state)
1524 
1525     This signal is emitted when the state of the connection changes.
1526     The argument \a state is the new state of the connection; it is
1527     one of the \l State values.
1528 
1529     It is usually emitted in response to a connectToHost() or close()
1530     command, but it can also be emitted "spontaneously", e.g. when the
1531     server closes the connection unexpectedly.
1532 
1533     \sa connectToHost() close() state() State
1534 */
1535 
1536 /*!
1537     \fn void QFtp::listInfo(const QUrlInfo &i);
1538 
1539     This signal is emitted for each directory entry the list() command
1540     finds. The details of the entry are stored in \a i.
1541 
1542     \sa list()
1543 */
1544 
1545 /*!
1546     \fn void QFtp::commandStarted(int id)
1547 
1548     This signal is emitted when processing the command identified by
1549     \a id starts.
1550 
1551     \sa commandFinished() done()
1552 */
1553 
1554 /*!
1555     \fn void QFtp::commandFinished(int id, bool error)
1556 
1557     This signal is emitted when processing the command identified by
1558     \a id has finished. \a error is true if an error occurred during
1559     the processing; otherwise \a error is false.
1560 
1561     \sa commandStarted() done() error() errorString()
1562 */
1563 
1564 /*!
1565     \fn void QFtp::done(bool error)
1566 
1567     This signal is emitted when the last pending command has finished;
1568     (it is emitted after the last command's commandFinished() signal).
1569     \a error is true if an error occurred during the processing;
1570     otherwise \a error is false.
1571 
1572     \sa commandFinished() error() errorString()
1573 */
1574 
1575 /*!
1576     \fn void QFtp::readyRead()
1577 
1578     This signal is emitted in response to a get() command when there
1579     is new data to read.
1580 
1581     If you specify a device as the second argument in the get()
1582     command, this signal is \e not emitted; instead the data is
1583     written directly to the device.
1584 
1585     You can read the data with the readAll() or read() functions.
1586 
1587     This signal is useful if you want to process the data in chunks as
1588     soon as it becomes available. If you are only interested in the
1589     complete data, just connect to the commandFinished() signal and
1590     read the data then instead.
1591 
1592     \sa get() read() readAll() bytesAvailable()
1593 */
1594 
1595 /*!
1596     \fn void QFtp::dataTransferProgress(qint64 done, qint64 total)
1597 
1598     This signal is emitted in response to a get() or put() request to
1599     indicate the current progress of the download or upload.
1600 
1601     \a done is the amount of data that has already been transferred
1602     and \a total is the total amount of data to be read or written. It
1603     is possible that the QFtp class is not able to determine the total
1604     amount of data that should be transferred, in which case \a total
1605     is 0. (If you connect this signal to a QProgressBar, the progress
1606     bar shows a busy indicator if the total is 0).
1607 
1608     \warning \a done and \a total are not necessarily the size in
1609     bytes, since for large files these values might need to be
1610     "scaled" to avoid overflow.
1611 
1612     \sa get(), put(), QProgressBar
1613 */
1614 
1615 /*!
1616     \fn void QFtp::rawCommandReply(int replyCode, const QString &detail);
1617 
1618     This signal is emitted in response to the rawCommand() function.
1619     \a replyCode is the 3 digit reply code and \a detail is the text
1620     that follows the reply code.
1621 
1622     \sa rawCommand()
1623 */
1624 
1625 /*!
1626     Connects to the FTP server \a host using port \a port.
1627 
1628     The stateChanged() signal is emitted when the state of the
1629     connecting process changes, e.g. to \c HostLookup, then \c
1630     Connecting, then \c Connected.
1631 
1632     The function does not block and returns immediately. The command
1633     is scheduled, and its execution is performed asynchronously. The
1634     function returns a unique identifier which is passed by
1635     commandStarted() and commandFinished().
1636 
1637     When the command is started the commandStarted() signal is
1638     emitted. When it is finished the commandFinished() signal is
1639     emitted.
1640 
1641     \sa stateChanged() commandStarted() commandFinished()
1642 */
connectToHost(const QString & host,quint16 port)1643 int QFtp::connectToHost(const QString &host, quint16 port)
1644 {
1645     QStringList cmds;
1646     cmds << host;
1647     cmds << QString::number((uint)port);
1648     int id = d->addCommand(new QFtpCommand(ConnectToHost, cmds));
1649     d->pi.transferConnectionExtended = true;
1650     return id;
1651 }
1652 
1653 /*!
1654     Logs in to the FTP server with the username \a user and the
1655     password \a password.
1656 
1657     The stateChanged() signal is emitted when the state of the
1658     connecting process changes, e.g. to \c LoggedIn.
1659 
1660     The function does not block and returns immediately. The command
1661     is scheduled, and its execution is performed asynchronously. The
1662     function returns a unique identifier which is passed by
1663     commandStarted() and commandFinished().
1664 
1665     When the command is started the commandStarted() signal is
1666     emitted. When it is finished the commandFinished() signal is
1667     emitted.
1668 
1669     \sa commandStarted() commandFinished()
1670 */
login(const QString & user,const QString & password)1671 int QFtp::login(const QString &user, const QString &password)
1672 {
1673     QStringList cmds;
1674     cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n"));
1675     cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n"));
1676     return d->addCommand(new QFtpCommand(Login, cmds));
1677 }
1678 
1679 /*!
1680     Closes the connection to the FTP server.
1681 
1682     The stateChanged() signal is emitted when the state of the
1683     connecting process changes, e.g. to \c Closing, then \c
1684     Unconnected.
1685 
1686     The function does not block and returns immediately. The command
1687     is scheduled, and its execution is performed asynchronously. The
1688     function returns a unique identifier which is passed by
1689     commandStarted() and commandFinished().
1690 
1691     When the command is started the commandStarted() signal is
1692     emitted. When it is finished the commandFinished() signal is
1693     emitted.
1694 
1695     \sa stateChanged() commandStarted() commandFinished()
1696 */
close()1697 int QFtp::close()
1698 {
1699     return d->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n"))));
1700 }
1701 
1702 /*!
1703     Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.
1704 
1705     \sa QFtp::TransferMode
1706 */
setTransferMode(TransferMode mode)1707 int QFtp::setTransferMode(TransferMode mode)
1708 {
1709     int id = d->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
1710     d->pi.transferConnectionExtended = true;
1711     d->transferMode = mode;
1712     return id;
1713 }
1714 
1715 /*!
1716     Enables use of the FTP proxy on host \a host and port \a
1717     port. Calling this function with \a host empty disables proxying.
1718 
1719     QFtp does not support FTP-over-HTTP proxy servers. Use
1720     QNetworkAccessManager for this.
1721 */
setProxy(const QString & host,quint16 port)1722 int QFtp::setProxy(const QString &host, quint16 port)
1723 {
1724     QStringList args;
1725     args << host << QString::number(port);
1726     return d->addCommand(new QFtpCommand(SetProxy, args));
1727 }
1728 
1729 /*!
1730     Lists the contents of directory \a dir on the FTP server. If \a
1731     dir is empty, it lists the contents of the current directory.
1732 
1733     The listInfo() signal is emitted for each directory entry found.
1734 
1735     The function does not block and returns immediately. The command
1736     is scheduled, and its execution is performed asynchronously. The
1737     function returns a unique identifier which is passed by
1738     commandStarted() and commandFinished().
1739 
1740     When the command is started the commandStarted() signal is
1741     emitted. When it is finished the commandFinished() signal is
1742     emitted.
1743 
1744     \sa listInfo() commandStarted() commandFinished()
1745 */
list(const QString & dir)1746 int QFtp::list(const QString &dir)
1747 {
1748     QStringList cmds;
1749     cmds << QLatin1String("TYPE A\r\n");
1750     cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
1751     if (dir.isEmpty())
1752         cmds << QLatin1String("LIST\r\n");
1753     else
1754         cmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));
1755     return d->addCommand(new QFtpCommand(List, cmds));
1756 }
1757 
1758 /*!
1759     Changes the working directory of the server to \a dir.
1760 
1761     The function does not block and returns immediately. The command
1762     is scheduled, and its execution is performed asynchronously. The
1763     function returns a unique identifier which is passed by
1764     commandStarted() and commandFinished().
1765 
1766     When the command is started the commandStarted() signal is
1767     emitted. When it is finished the commandFinished() signal is
1768     emitted.
1769 
1770     \sa commandStarted() commandFinished()
1771 */
cd(const QString & dir)1772 int QFtp::cd(const QString &dir)
1773 {
1774     return d->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n"))));
1775 }
1776 
1777 /*!
1778     Downloads the file \a file from the server.
1779 
1780     If \a dev is 0, then the readyRead() signal is emitted when there
1781     is data available to read. You can then read the data with the
1782     read() or readAll() functions.
1783 
1784     If \a dev is not 0, the data is written directly to the device \a
1785     dev. Make sure that the \a dev pointer is valid for the duration
1786     of the operation (it is safe to delete it when the
1787     commandFinished() signal is emitted). In this case the readyRead()
1788     signal is \e not emitted and you cannot read data with the
1789     read() or readAll() functions.
1790 
1791     If you don't read the data immediately it becomes available, i.e.
1792     when the readyRead() signal is emitted, it is still available
1793     until the next command is started.
1794 
1795     For example, if you want to present the data to the user as soon
1796     as there is something available, connect to the readyRead() signal
1797     and read the data immediately. On the other hand, if you only want
1798     to work with the complete data, you can connect to the
1799     commandFinished() signal and read the data when the get() command
1800     is finished.
1801 
1802     The data is transferred as Binary or Ascii depending on the value
1803     of \a type.
1804 
1805     The function does not block and returns immediately. The command
1806     is scheduled, and its execution is performed asynchronously. The
1807     function returns a unique identifier which is passed by
1808     commandStarted() and commandFinished().
1809 
1810     When the command is started the commandStarted() signal is
1811     emitted. When it is finished the commandFinished() signal is
1812     emitted.
1813 
1814     \sa readyRead() dataTransferProgress() commandStarted()
1815     commandFinished()
1816 */
get(const QString & file,QIODevice * dev,TransferType type)1817 int QFtp::get(const QString &file, QIODevice *dev, TransferType type)
1818 {
1819     QStringList cmds;
1820     if (type == Binary)
1821         cmds << QLatin1String("TYPE I\r\n");
1822     else
1823         cmds << QLatin1String("TYPE A\r\n");
1824     cmds << QLatin1String("SIZE ") + file + QLatin1String("\r\n");
1825     cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
1826     cmds << QLatin1String("RETR ") + file + QLatin1String("\r\n");
1827     return d->addCommand(new QFtpCommand(Get, cmds, dev));
1828 }
1829 
1830 /*!
1831     \overload
1832 
1833     Writes a copy of the given \a data to the file called \a file on
1834     the server. The progress of the upload is reported by the
1835     dataTransferProgress() signal.
1836 
1837     The data is transferred as Binary or Ascii depending on the value
1838     of \a type.
1839 
1840     The function does not block and returns immediately. The command
1841     is scheduled, and its execution is performed asynchronously. The
1842     function returns a unique identifier which is passed by
1843     commandStarted() and commandFinished().
1844 
1845     When the command is started the commandStarted() signal is
1846     emitted. When it is finished the commandFinished() signal is
1847     emitted.
1848 
1849     Since this function takes a copy of the \a data, you can discard
1850     your own copy when this function returns.
1851 
1852     \sa dataTransferProgress() commandStarted() commandFinished()
1853 */
put(const QByteArray & data,const QString & file,TransferType type)1854 int QFtp::put(const QByteArray &data, const QString &file, TransferType type)
1855 {
1856     QStringList cmds;
1857     if (type == Binary)
1858         cmds << QLatin1String("TYPE I\r\n");
1859     else
1860         cmds << QLatin1String("TYPE A\r\n");
1861     cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
1862     cmds << QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n");
1863     cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
1864     return d->addCommand(new QFtpCommand(Put, cmds, data));
1865 }
1866 
1867 /*!
1868     Reads the data from the IO device \a dev, and writes it to the
1869     file called \a file on the server. The data is read in chunks from
1870     the IO device, so this overload allows you to transmit large
1871     amounts of data without the need to read all the data into memory
1872     at once.
1873 
1874     The data is transferred as Binary or Ascii depending on the value
1875     of \a type.
1876 
1877     Make sure that the \a dev pointer is valid for the duration of the
1878     operation (it is safe to delete it when the commandFinished() is
1879     emitted).
1880 */
put(QIODevice * dev,const QString & file,TransferType type)1881 int QFtp::put(QIODevice *dev, const QString &file, TransferType type)
1882 {
1883     QStringList cmds;
1884     if (type == Binary)
1885         cmds << QLatin1String("TYPE I\r\n");
1886     else
1887         cmds << QLatin1String("TYPE A\r\n");
1888     cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
1889     if (!dev->isSequential())
1890         cmds << QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n");
1891     cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");
1892     return d->addCommand(new QFtpCommand(Put, cmds, dev));
1893 }
1894 
1895 /*!
1896     Deletes the file called \a file from the server.
1897 
1898     The function does not block and returns immediately. The command
1899     is scheduled, and its execution is performed asynchronously. The
1900     function returns a unique identifier which is passed by
1901     commandStarted() and commandFinished().
1902 
1903     When the command is started the commandStarted() signal is
1904     emitted. When it is finished the commandFinished() signal is
1905     emitted.
1906 
1907     \sa commandStarted() commandFinished()
1908 */
remove(const QString & file)1909 int QFtp::remove(const QString &file)
1910 {
1911     return d->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n"))));
1912 }
1913 
1914 /*!
1915     Creates a directory called \a dir on the server.
1916 
1917     The function does not block and returns immediately. The command
1918     is scheduled, and its execution is performed asynchronously. The
1919     function returns a unique identifier which is passed by
1920     commandStarted() and commandFinished().
1921 
1922     When the command is started the commandStarted() signal is
1923     emitted. When it is finished the commandFinished() signal is
1924     emitted.
1925 
1926     \sa commandStarted() commandFinished()
1927 */
mkdir(const QString & dir)1928 int QFtp::mkdir(const QString &dir)
1929 {
1930     return d->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n"))));
1931 }
1932 
1933 /*!
1934     Removes the directory called \a dir from the server.
1935 
1936     The function does not block and returns immediately. The command
1937     is scheduled, and its execution is performed asynchronously. The
1938     function returns a unique identifier which is passed by
1939     commandStarted() and commandFinished().
1940 
1941     When the command is started the commandStarted() signal is
1942     emitted. When it is finished the commandFinished() signal is
1943     emitted.
1944 
1945     \sa commandStarted() commandFinished()
1946 */
rmdir(const QString & dir)1947 int QFtp::rmdir(const QString &dir)
1948 {
1949     return d->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n"))));
1950 }
1951 
1952 /*!
1953     Renames the file called \a oldname to \a newname on the server.
1954 
1955     The function does not block and returns immediately. The command
1956     is scheduled, and its execution is performed asynchronously. The
1957     function returns a unique identifier which is passed by
1958     commandStarted() and commandFinished().
1959 
1960     When the command is started the commandStarted() signal is
1961     emitted. When it is finished the commandFinished() signal is
1962     emitted.
1963 
1964     \sa commandStarted() commandFinished()
1965 */
rename(const QString & oldname,const QString & newname)1966 int QFtp::rename(const QString &oldname, const QString &newname)
1967 {
1968     QStringList cmds;
1969     cmds << QLatin1String("RNFR ") + oldname + QLatin1String("\r\n");
1970     cmds << QLatin1String("RNTO ") + newname + QLatin1String("\r\n");
1971     return d->addCommand(new QFtpCommand(Rename, cmds));
1972 }
1973 
1974 /*!
1975     Sends the raw FTP command \a command to the FTP server. This is
1976     useful for low-level FTP access. If the operation you wish to
1977     perform has an equivalent QFtp function, we recommend using the
1978     function instead of raw FTP commands since the functions are
1979     easier and safer.
1980 
1981     The function does not block and returns immediately. The command
1982     is scheduled, and its execution is performed asynchronously. The
1983     function returns a unique identifier which is passed by
1984     commandStarted() and commandFinished().
1985 
1986     When the command is started the commandStarted() signal is
1987     emitted. When it is finished the commandFinished() signal is
1988     emitted.
1989 
1990     \sa rawCommandReply() commandStarted() commandFinished()
1991 */
rawCommand(const QString & command)1992 int QFtp::rawCommand(const QString &command)
1993 {
1994     QString cmd = command.trimmed() + QLatin1String("\r\n");
1995     return d->addCommand(new QFtpCommand(RawCommand, QStringList(cmd)));
1996 }
1997 
1998 /*!
1999     Returns the number of bytes that can be read from the data socket
2000     at the moment.
2001 
2002     \sa get() readyRead() read() readAll()
2003 */
bytesAvailable() const2004 qint64 QFtp::bytesAvailable() const
2005 {
2006     return d->pi.dtp.bytesAvailable();
2007 }
2008 
2009 /*! \fn qint64 QFtp::readBlock(char *data, quint64 maxlen)
2010 
2011     Use read() instead.
2012 */
2013 
2014 /*!
2015     Reads \a maxlen bytes from the data socket into \a data and
2016     returns the number of bytes read. Returns -1 if an error occurred.
2017 
2018     \sa get() readyRead() bytesAvailable() readAll()
2019 */
read(char * data,qint64 maxlen)2020 qint64 QFtp::read(char *data, qint64 maxlen)
2021 {
2022     return d->pi.dtp.read(data, maxlen);
2023 }
2024 
2025 /*!
2026     Reads all the bytes available from the data socket and returns
2027     them.
2028 
2029     \sa get() readyRead() bytesAvailable() read()
2030 */
readAll()2031 QByteArray QFtp::readAll()
2032 {
2033     return d->pi.dtp.readAll();
2034 }
2035 
2036 /*!
2037     Aborts the current command and deletes all scheduled commands.
2038 
2039     If there is an unfinished command (i.e. a command for which the
2040     commandStarted() signal has been emitted, but for which the
2041     commandFinished() signal has not been emitted), this function
2042     sends an \c ABORT command to the server. When the server replies
2043     that the command is aborted, the commandFinished() signal with the
2044     \c error argument set to \c true is emitted for the command. Due
2045     to timing issues, it is possible that the command had already
2046     finished before the abort request reached the server, in which
2047     case, the commandFinished() signal is emitted with the \c error
2048     argument set to \c false.
2049 
2050     For all other commands that are affected by the abort(), no
2051     signals are emitted.
2052 
2053     If you don't start further FTP commands directly after the
2054     abort(), there won't be any scheduled commands and the done()
2055     signal is emitted.
2056 
2057     \warning Some FTP servers, for example the BSD FTP daemon (version
2058     0.3), wrongly return a positive reply even when an abort has
2059     occurred. For these servers the commandFinished() signal has its
2060     error flag set to \c false, even though the command did not
2061     complete successfully.
2062 
2063     \sa clearPendingCommands()
2064 */
abort()2065 void QFtp::abort()
2066 {
2067     if (d->pending.isEmpty())
2068         return;
2069 
2070     clearPendingCommands();
2071     d->pi.abort();
2072 }
2073 
2074 /*!
2075     Returns the identifier of the FTP command that is being executed
2076     or 0 if there is no command being executed.
2077 
2078     \sa currentCommand()
2079 */
currentId() const2080 int QFtp::currentId() const
2081 {
2082     if (d->pending.isEmpty())
2083         return 0;
2084     return d->pending.first()->id;
2085 }
2086 
2087 /*!
2088     Returns the command type of the FTP command being executed or \c
2089     None if there is no command being executed.
2090 
2091     \sa currentId()
2092 */
currentCommand() const2093 QFtp::Command QFtp::currentCommand() const
2094 {
2095     if (d->pending.isEmpty())
2096         return None;
2097     return d->pending.first()->command;
2098 }
2099 
2100 /*!
2101     Returns the QIODevice pointer that is used by the FTP command to read data
2102     from or store data to. If there is no current FTP command being executed or
2103     if the command does not use an IO device, this function returns 0.
2104 
2105     This function can be used to delete the QIODevice in the slot connected to
2106     the commandFinished() signal.
2107 
2108     \sa get() put()
2109 */
currentDevice() const2110 QIODevice* QFtp::currentDevice() const
2111 {
2112     if (d->pending.isEmpty())
2113         return 0;
2114     QFtpCommand *c = d->pending.first();
2115     if (c->is_ba)
2116         return 0;
2117     return c->data.dev;
2118 }
2119 
2120 /*!
2121     Returns true if there are any commands scheduled that have not yet
2122     been executed; otherwise returns false.
2123 
2124     The command that is being executed is \e not considered as a
2125     scheduled command.
2126 
2127     \sa clearPendingCommands() currentId() currentCommand()
2128 */
hasPendingCommands() const2129 bool QFtp::hasPendingCommands() const
2130 {
2131     return d->pending.count() > 1;
2132 }
2133 
2134 /*!
2135     Deletes all pending commands from the list of scheduled commands.
2136     This does not affect the command that is being executed. If you
2137     want to stop this as well, use abort().
2138 
2139     \sa hasPendingCommands() abort()
2140 */
clearPendingCommands()2141 void QFtp::clearPendingCommands()
2142 {
2143     // delete all entires except the first one
2144     while (d->pending.count() > 1)
2145         delete d->pending.takeLast();
2146 }
2147 
2148 /*!
2149     Returns the current state of the object. When the state changes,
2150     the stateChanged() signal is emitted.
2151 
2152     \sa State stateChanged()
2153 */
state() const2154 QFtp::State QFtp::state() const
2155 {
2156     return d->state;
2157 }
2158 
2159 /*!
2160     Returns the last error that occurred. This is useful to find out
2161     what went wrong when receiving a commandFinished() or a done()
2162     signal with the \c error argument set to \c true.
2163 
2164     If you start a new command, the error status is reset to \c NoError.
2165 */
error() const2166 QFtp::Error QFtp::error() const
2167 {
2168     return d->error;
2169 }
2170 
2171 /*!
2172     Returns a human-readable description of the last error that
2173     occurred. This is useful for presenting a error message to the
2174     user when receiving a commandFinished() or a done() signal with
2175     the \c error argument set to \c true.
2176 
2177     The error string is often (but not always) the reply from the
2178     server, so it is not always possible to translate the string. If
2179     the message comes from Qt, the string has already passed through
2180     tr().
2181 */
errorString() const2182 QString QFtp::errorString() const
2183 {
2184     return d->errorString;
2185 }
2186 
2187 /*! \internal
2188 */
_q_startNextCommand()2189 void QFtpPrivate::_q_startNextCommand()
2190 {
2191     Q_Q(QFtp);
2192     if (pending.isEmpty())
2193         return;
2194     QFtpCommand *c = pending.first();
2195 
2196     error = QFtp::NoError;
2197     errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));
2198 
2199     if (q->bytesAvailable())
2200         q->readAll(); // clear the data
2201     emit q->commandStarted(c->id);
2202 
2203     // Proxy support, replace the Login argument in place, then fall
2204     // through.
2205     if (c->command == QFtp::Login && !proxyHost.isEmpty()) {
2206         QString loginString = c->rawCmds.first().trimmed();
2207         loginString += QLatin1Char('@') + host;
2208         if (port && port != 21)
2209             loginString += QLatin1Char(':') + QString::number(port);
2210         loginString += QLatin1String("\r\n");
2211         c->rawCmds[0] = loginString;
2212     }
2213 
2214     if (c->command == QFtp::SetTransferMode) {
2215         _q_piFinished(QLatin1String("Transfer mode set"));
2216     } else if (c->command == QFtp::SetProxy) {
2217         proxyHost = c->rawCmds[0];
2218         proxyPort = c->rawCmds[1].toUInt();
2219         c->rawCmds.clear();
2220         _q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));
2221     } else if (c->command == QFtp::ConnectToHost) {
2222 #ifndef QT_NO_BEARERMANAGEMENT
2223         //copy network session down to the PI
2224         pi.setProperty("_q_networksession", q->property("_q_networksession"));
2225 #endif
2226         if (!proxyHost.isEmpty()) {
2227             host = c->rawCmds[0];
2228             port = c->rawCmds[1].toUInt();
2229             pi.connectToHost(proxyHost, proxyPort);
2230         } else {
2231             pi.connectToHost(c->rawCmds[0], c->rawCmds[1].toUInt());
2232         }
2233     } else {
2234         if (c->command == QFtp::Put) {
2235             if (c->is_ba) {
2236                 pi.dtp.setData(c->data.ba);
2237                 pi.dtp.setBytesTotal(c->data.ba->size());
2238             } else if (c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly))) {
2239                 pi.dtp.setDevice(c->data.dev);
2240                 if (c->data.dev->isSequential()) {
2241                     pi.dtp.setBytesTotal(0);
2242                     pi.dtp.connect(c->data.dev, SIGNAL(readyRead()), SLOT(dataReadyRead()));
2243                     pi.dtp.connect(c->data.dev, SIGNAL(readChannelFinished()), SLOT(dataReadyRead()));
2244                 } else {
2245                     pi.dtp.setBytesTotal(c->data.dev->size());
2246                 }
2247             }
2248         } else if (c->command == QFtp::Get) {
2249             if (!c->is_ba && c->data.dev) {
2250                 pi.dtp.setDevice(c->data.dev);
2251             }
2252         } else if (c->command == QFtp::Close) {
2253             state = QFtp::Closing;
2254             emit q->stateChanged(state);
2255         }
2256         pi.sendCommands(c->rawCmds);
2257     }
2258 }
2259 
2260 /*! \internal
2261 */
_q_piFinished(const QString &)2262 void QFtpPrivate::_q_piFinished(const QString&)
2263 {
2264     if (pending.isEmpty())
2265         return;
2266     QFtpCommand *c = pending.first();
2267 
2268     if (c->command == QFtp::Close) {
2269         // The order of in which the slots are called is arbitrary, so
2270         // disconnect the SIGNAL-SIGNAL temporary to make sure that we
2271         // don't get the commandFinished() signal before the stateChanged()
2272         // signal.
2273         if (state != QFtp::Unconnected) {
2274             close_waitForStateChange = true;
2275             return;
2276         }
2277     }
2278     emit q_func()->commandFinished(c->id, false);
2279     pending.removeFirst();
2280 
2281     delete c;
2282 
2283     if (pending.isEmpty()) {
2284         emit q_func()->done(false);
2285     } else {
2286         _q_startNextCommand();
2287     }
2288 }
2289 
2290 /*! \internal
2291 */
_q_piError(int errorCode,const QString & text)2292 void QFtpPrivate::_q_piError(int errorCode, const QString &text)
2293 {
2294     Q_Q(QFtp);
2295 
2296     if (pending.isEmpty()) {
2297         qWarning("QFtpPrivate::_q_piError was called without pending command!");
2298         return;
2299     }
2300 
2301     QFtpCommand *c = pending.first();
2302 
2303     // non-fatal errors
2304     if (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {
2305         pi.dtp.setBytesTotal(0);
2306         return;
2307     } else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {
2308         return;
2309     }
2310 
2311     error = QFtp::Error(errorCode);
2312     switch (q->currentCommand()) {
2313         case QFtp::ConnectToHost:
2314             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1"))
2315                           .arg(text);
2316             break;
2317         case QFtp::Login:
2318             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1"))
2319                           .arg(text);
2320             break;
2321         case QFtp::List:
2322             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1"))
2323                           .arg(text);
2324             break;
2325         case QFtp::Cd:
2326             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1"))
2327                           .arg(text);
2328             break;
2329         case QFtp::Get:
2330             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1"))
2331                           .arg(text);
2332             break;
2333         case QFtp::Put:
2334             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1"))
2335                           .arg(text);
2336             break;
2337         case QFtp::Remove:
2338             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1"))
2339                           .arg(text);
2340             break;
2341         case QFtp::Mkdir:
2342             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1"))
2343                           .arg(text);
2344             break;
2345         case QFtp::Rmdir:
2346             errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1"))
2347                           .arg(text);
2348             break;
2349         default:
2350             errorString = text;
2351             break;
2352     }
2353 
2354     pi.clearPendingCommands();
2355     q->clearPendingCommands();
2356     emit q->commandFinished(c->id, true);
2357 
2358     pending.removeFirst();
2359     delete c;
2360     if (pending.isEmpty())
2361         emit q->done(true);
2362     else
2363         _q_startNextCommand();
2364 }
2365 
2366 /*! \internal
2367 */
_q_piConnectState(int connectState)2368 void QFtpPrivate::_q_piConnectState(int connectState)
2369 {
2370     state = QFtp::State(connectState);
2371     emit q_func()->stateChanged(state);
2372     if (close_waitForStateChange) {
2373         close_waitForStateChange = false;
2374         _q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));
2375     }
2376 }
2377 
2378 /*! \internal
2379 */
_q_piFtpReply(int code,const QString & text)2380 void QFtpPrivate::_q_piFtpReply(int code, const QString &text)
2381 {
2382     if (q_func()->currentCommand() == QFtp::RawCommand) {
2383         pi.rawCommand = true;
2384         emit q_func()->rawCommandReply(code, text);
2385     }
2386 }
2387 
2388 /*!
2389     Destructor.
2390 */
~QFtp()2391 QFtp::~QFtp()
2392 {
2393     abort();
2394     close();
2395 }
2396 
2397 QT_END_NAMESPACE
2398 
2399 #include "qftp.moc"
2400 
2401 #include "moc_qftp.cpp"
2402 
2403 #endif // QT_NO_FTP
2404