1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 //#define QFTPPI_DEBUG
43 //#define QFTPDTP_DEBUG
45 #include "private/qftp_p.h"
46 #include "qabstractsocket.h"
50 #include "qcoreapplication.h"
51 #include "qtcpsocket.h"
53 #include "qstringlist.h"
56 #include "qfileinfo.h"
58 #include "qtcpserver.h"
66 The QFtpDTP (DTP = Data Transfer Process) controls all client side
67 data transfer between the client and server.
69 class QFtpDTP : public QObject
82 QFtpDTP(QFtpPI *p, QObject *parent = 0);
84 void setData(QByteArray *);
85 void setDevice(QIODevice *);
87 void setBytesTotal(qint64 bytes);
89 bool hasError() const;
90 QString errorMessage() const;
93 void connectToHost(const QString & host, quint16 port);
94 int setupListener(const QHostAddress &address);
95 void waitForConnection();
97 QTcpSocket::SocketState state() const;
98 qint64 bytesAvailable() const;
99 qint64 read(char *data, qint64 maxlen);
100 QByteArray readAll();
102 void abortConnection();
104 static bool parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info);
107 void listInfo(const QUrlInfo&);
109 void dataTransferProgress(qint64, qint64);
111 void connectState(int);
114 void socketConnected();
115 void socketReadyRead();
116 void socketError(QAbstractSocket::SocketError);
117 void socketConnectionClosed();
118 void socketBytesWritten(qint64);
121 void dataReadyRead();
135 // If is_ba is true, ba is used; ba is never 0.
136 // Otherwise dev is used; dev can be 0 or not.
143 QByteArray bytesFromSocket;
146 /**********************************************************************
148 * QFtpPI - Protocol Interpreter
150 *********************************************************************/
152 class QFtpPI : public QObject
157 QFtpPI(QObject *parent = 0);
159 void connectToHost(const QString &host, quint16 port);
161 bool sendCommands(const QStringList &cmds);
162 bool sendCommand(const QString &cmd)
163 { return sendCommands(QStringList(cmd)); }
165 void clearPendingCommands();
168 QString currentCommand() const
169 { return currentCmd; }
172 bool transferConnectionExtended;
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
177 void connectState(int);
178 void finished(const QString&);
179 void error(int, const QString&);
180 void rawFtpReply(int, const QString&);
185 void connectionClosed();
186 void delayedCloseFinished();
188 void error(QAbstractSocket::SocketError);
190 void dtpConnectState(int);
193 // the states are modelled after the generalized state diagram of RFC 959,
212 QTcpSocket commandSocket;
216 AbortState abortState;
217 QStringList pendingCommands;
220 bool waitForDtpToConnect;
221 bool waitForDtpToClose;
223 QByteArray bytesFromSocket;
225 friend class QFtpDTP;
228 /**********************************************************************
230 * QFtpCommand implemenatation
232 *********************************************************************/
236 QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba);
237 QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev = 0);
241 QFtp::Command command;
244 // If is_ba is true, ba is used; ba is never 0.
245 // Otherwise dev is used; dev can be 0 or not.
252 static QBasicAtomicInt idCounter;
255 QBasicAtomicInt QFtpCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
257 QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba)
258 : command(cmd), rawCmds(raw), is_ba(true)
260 id = idCounter.fetchAndAddRelaxed(1);
261 data.ba = new QByteArray(ba);
264 QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev)
265 : command(cmd), rawCmds(raw), is_ba(false)
267 id = idCounter.fetchAndAddRelaxed(1);
271 QFtpCommand::~QFtpCommand()
277 /**********************************************************************
279 * QFtpDTP implemenatation
281 *********************************************************************/
282 QFtpDTP::QFtpDTP(QFtpPI *p, QObject *parent) :
290 listener.setObjectName(QLatin1String("QFtpDTP active state server"));
291 connect(&listener, SIGNAL(newConnection()), SLOT(setupSocket()));
294 void QFtpDTP::setData(QByteArray *ba)
300 void QFtpDTP::setDevice(QIODevice *dev)
306 void QFtpDTP::setBytesTotal(qint64 bytes)
310 emit dataTransferProgress(bytesDone, bytesTotal);
313 void QFtpDTP::connectToHost(const QString & host, quint16 port)
315 bytesFromSocket.clear();
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"));
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)));
333 socket->connectToHost(host, port);
336 int QFtpDTP::setupListener(const QHostAddress &address)
338 #ifndef QT_NO_BEARERMANAGEMENT
339 //copy network session down to the socket
340 listener.setProperty("_q_networksession", property("_q_networksession"));
342 if (!listener.isListening() && !listener.listen(address, 0))
344 return listener.serverPort();
347 void QFtpDTP::waitForConnection()
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();
356 QTcpSocket::SocketState QFtpDTP::state() const
358 return socket ? socket->state() : QTcpSocket::UnconnectedState;
361 qint64 QFtpDTP::bytesAvailable() const
363 if (!socket || socket->state() != QTcpSocket::ConnectedState)
364 return (qint64) bytesFromSocket.size();
365 return socket->bytesAvailable();
368 qint64 QFtpDTP::read(char *data, qint64 maxlen)
371 if (socket && socket->state() == QTcpSocket::ConnectedState) {
372 read = socket->read(data, maxlen);
374 read = qMin(maxlen, qint64(bytesFromSocket.size()));
375 memcpy(data, bytesFromSocket.data(), read);
376 bytesFromSocket.remove(0, read);
383 QByteArray QFtpDTP::readAll()
386 if (socket && socket->state() == QTcpSocket::ConnectedState) {
387 tmp = socket->readAll();
388 bytesDone += tmp.size();
390 tmp = bytesFromSocket;
391 bytesFromSocket.clear();
396 void QFtpDTP::writeData()
402 #if defined(QFTPDTP_DEBUG)
403 qDebug("QFtpDTP::writeData: write %d bytes", data.ba->size());
405 if (data.ba->size() == 0)
406 emit dataTransferProgress(0, bytesTotal);
408 socket->write(data.ba->data(), data.ba->size());
413 } else if (data.dev) {
414 callWriteData = false;
415 const qint64 blockSize = 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);
422 socket->write(buf, read);
423 } else if (read == -1 || (!data.dev->isSequential() && data.dev->atEnd())) {
425 if (bytesDone == 0 && socket->bytesToWrite() == 0)
426 emit dataTransferProgress(0, bytesTotal);
431 // do we continue uploading?
432 callWriteData = data.dev != 0;
436 void QFtpDTP::dataReadyRead()
441 inline bool QFtpDTP::hasError() const
443 return !err.isNull();
446 inline QString QFtpDTP::errorMessage() const
451 inline void QFtpDTP::clearError()
456 void QFtpDTP::abortConnection()
458 #if defined(QFTPDTP_DEBUG)
459 qDebug("QFtpDTP::abortConnection, bytesAvailable == %lli",
460 socket ? socket->bytesAvailable() : (qint64) 0);
462 callWriteData = false;
469 static void _q_fixupDateTime(QDateTime *dateTime)
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);
480 static void _q_parseUnixDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
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)
489 char first = tokens.at(1).at(0).toLatin1();
492 info->setFile(false);
493 info->setSymLink(false);
494 } else if (first == '-') {
497 info->setSymLink(false);
498 } else if (first == 'l') {
500 info->setFile(false);
501 info->setSymLink(true);
505 QString name = tokens.at(7);
506 if (info->isSymLink()) {
507 int linkPos = name.indexOf(QLatin1String(" ->"));
509 name.resize(linkPos);
513 // Resolve owner & group
514 info->setOwner(tokens.at(3));
515 info->setGroup(tokens.at(4));
518 info->setSize(tokens.at(5).toLongLong());
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");
524 QString dateString = tokens.at(6);
525 dateString[0] = dateString[0].toUpper();
527 // Resolve the modification date by parsing all possible formats
530 #ifndef QT_NO_DATESTRING
532 dateTime = QLocale::c().toDateTime(dateString, formats.at(n++));
533 } while (n < formats.size() && (!dateTime.isValid()));
536 if (n == 2 || n == 4) {
538 dateTime.setDate(QDate(QDate::currentDate().year(),
539 dateTime.date().month(),
540 dateTime.date().day()));
541 _q_fixupDateTime(&dateTime);
543 if (dateTime.isValid())
544 info->setLastModified(dateTime);
546 // Resolve permissions
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);
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));
565 static void _q_parseDosDir(const QStringList &tokens, const QString &userName, QUrlInfo *info)
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)
575 QString name = tokens.at(3);
577 info->setSymLink(name.toLower().endsWith(QLatin1String(".lnk")));
579 if (tokens.at(2) == QLatin1String("<DIR>")) {
580 info->setFile(false);
585 info->setSize(tokens.at(2).toLongLong());
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;
594 int extIndex = name.lastIndexOf(QLatin1Char('.'));
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);
601 info->setReadable(true);
602 info->setWritable(info->isFile());
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()));
614 info->setLastModified(dateTime);
618 bool QFtpDTP::parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info)
620 if (buffer.isEmpty())
623 QString bufferStr = QString::fromLatin1(buffer).trimmed();
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);
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);
645 void QFtpDTP::socketConnected()
648 #if defined(QFTPDTP_DEBUG)
649 qDebug("QFtpDTP::connectState(CsConnected)");
651 emit connectState(QFtpDTP::CsConnected);
654 void QFtpDTP::socketReadyRead()
659 if (pi->currentCommand().isEmpty()) {
661 #if defined(QFTPDTP_DEBUG)
662 qDebug("QFtpDTP::connectState(CsClosed)");
664 emit connectState(QFtpDTP::CsClosed);
668 if (pi->abortState != QFtpPI::None) {
674 if (pi->currentCommand().startsWith(QLatin1String("LIST"))) {
675 while (socket->canReadLine()) {
677 QByteArray line = socket->readLine();
678 #if defined(QFTPDTP_DEBUG)
679 qDebug("QFtpDTP read (list): '%s'", line.constData());
681 if (parseDir(line, QLatin1String(""), &i)) {
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);
692 if (!is_ba && data.dev) {
695 ba.resize(socket->bytesAvailable());
696 qint64 bytesRead = socket->read(ba.data(), ba.size());
698 // a read following a readyRead() signal will
702 ba.resize(bytesRead);
703 bytesDone += bytesRead;
704 #if defined(QFTPDTP_DEBUG)
705 qDebug("QFtpDTP read: %lli bytes (total %lli bytes)", bytesRead, bytesDone);
707 if (data.dev) // make sure it wasn't deleted in the slot
709 emit dataTransferProgress(bytesDone, bytesTotal);
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());
716 #if defined(QFTPDTP_DEBUG)
717 qDebug("QFtpDTP readyRead: %lli bytes available (total %lli bytes read)",
718 bytesAvailable(), bytesDone);
720 emit dataTransferProgress(bytesDone+socket->bytesAvailable(), bytesTotal);
726 void QFtpDTP::socketError(QAbstractSocket::SocketError e)
728 if (e == QTcpSocket::HostNotFoundError) {
729 #if defined(QFTPDTP_DEBUG)
730 qDebug("QFtpDTP::connectState(CsHostNotFound)");
732 emit connectState(QFtpDTP::CsHostNotFound);
733 } else if (e == QTcpSocket::ConnectionRefusedError) {
734 #if defined(QFTPDTP_DEBUG)
735 qDebug("QFtpDTP::connectState(CsConnectionRefused)");
737 emit connectState(QFtpDTP::CsConnectionRefused);
741 void QFtpDTP::socketConnectionClosed()
743 if (!is_ba && data.dev) {
747 bytesFromSocket = socket->readAll();
748 #if defined(QFTPDTP_DEBUG)
749 qDebug("QFtpDTP::connectState(CsClosed)");
751 emit connectState(QFtpDTP::CsClosed);
754 void QFtpDTP::socketBytesWritten(qint64 bytes)
757 #if defined(QFTPDTP_DEBUG)
758 qDebug("QFtpDTP::bytesWritten(%lli)", bytesDone);
760 emit dataTransferProgress(bytesDone, bytesTotal);
765 void QFtpDTP::setupSocket()
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)));
778 void QFtpDTP::clearData()
784 /**********************************************************************
786 * QFtpPI implemenatation
788 *********************************************************************/
789 QFtpPI::QFtpPI(QObject *parent) :
792 transferConnectionExtended(true),
795 state(Begin), abortState(None),
796 currentCmd(QString()),
797 waitForDtpToConnect(false),
798 waitForDtpToClose(false)
800 commandSocket.setObjectName(QLatin1String("QFtpPI_socket"));
801 connect(&commandSocket, SIGNAL(hostFound()),
803 connect(&commandSocket, SIGNAL(connected()),
805 connect(&commandSocket, SIGNAL(disconnected()),
806 SLOT(connectionClosed()));
807 connect(&commandSocket, SIGNAL(readyRead()),
809 connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),
810 SLOT(error(QAbstractSocket::SocketError)));
812 connect(&dtp, SIGNAL(connectState(int)),
813 SLOT(dtpConnectState(int)));
816 void QFtpPI::connectToHost(const QString &host, quint16 port)
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"));
824 commandSocket.connectToHost(host, port);
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.
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.
837 bool QFtpPI::sendCommands(const QStringList &cmds)
839 if (!pendingCommands.isEmpty())
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
847 pendingCommands = cmds;
852 void QFtpPI::clearPendingCommands()
854 pendingCommands.clear();
855 dtp.abortConnection();
862 pendingCommands.clear();
864 if (abortState != None)
868 if (currentCmd.isEmpty())
869 return; //no command in progress
871 if (currentCmd.startsWith(QLatin1String("STOR "))) {
872 abortState = AbortStarted;
873 #if defined(QFTPPI_DEBUG)
874 qDebug("QFtpPI send: ABOR");
876 commandSocket.write("ABOR\r\n", 6);
878 dtp.abortConnection();
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();
889 void QFtpPI::hostFound()
891 emit connectState(QFtp::Connecting);
894 void QFtpPI::connected()
897 #if defined(QFTPPI_DEBUG)
898 // qDebug("QFtpPI state: %d [connected()]", state);
900 // try to improve performance by setting TCP_NODELAY
901 commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
903 emit connectState(QFtp::Connected);
906 void QFtpPI::connectionClosed()
908 commandSocket.close();
909 emit connectState(QFtp::Unconnected);
912 void QFtpPI::delayedCloseFinished()
914 emit connectState(QFtp::Unconnected);
917 void QFtpPI::error(QAbstractSocket::SocketError e)
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()));
934 void QFtpPI::readyRead()
936 if (waitForDtpToClose)
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) {
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]) {
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);
966 while (lineLeft4 != endOfMultiLine) {
967 if (lineLeft4 == lineCont)
968 replyText += line.mid(4); // strip 'xyz-'
971 if (!commandSocket.canReadLine())
973 line = QString::fromLatin1(commandSocket.readLine());
974 lineLeft4 = line.left(4);
976 replyText += line.mid(4); // strip reply code 'xyz '
977 if (replyText.endsWith(QLatin1String("\r\n")))
981 replyText = QLatin1String("");
988 Process a reply from the FTP server.
990 Returns true if the reply was processed or false if the reply has to be
991 processed at a later point.
993 bool QFtpPI::processReply()
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());
1000 qDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]);
1003 int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];
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;
1014 switch (abortState) {
1016 abortState = WaitForAbortToFinish;
1018 case WaitForAbortToFinish:
1026 static const State table[5] = {
1027 /* 1yz 2yz 3yz 4yz 5yz */
1028 Waiting, Success, Idle, Failure, Failure
1032 if (replyCode[0] == 1) {
1034 } else if (replyCode[0] == 2) {
1036 emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));
1039 // reply codes not starting with 1 or 2 are not handled.
1042 if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)
1045 #if defined(Q_OS_IRIX) && defined(Q_CC_GNU)
1047 // work around a crash on 64 bit gcc IRIX
1048 State *t = (State *) table;
1049 state = t[replyCode[0] - 1];
1052 if (replyCodeInt == 202)
1055 state = table[replyCode[0] - 1];
1059 // ignore unrequested message
1062 #if defined(QFTPPI_DEBUG)
1063 // qDebug("QFtpPI state: %d [processReply() intermediate]", state);
1066 // special actions on certain replies
1067 emit rawFtpReply(replyCodeInt, replyText);
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
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");
1081 // this error should be reported
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);
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");
1096 // this error should be reported
1099 QChar delimiter = replyText.at(portPos);
1100 QStringList epsvParameters = replyText.mid(portPos).split(delimiter);
1102 waitForDtpToConnect = true;
1103 dtp.connectToHost(commandSocket.peerAddress().toString(),
1104 epsvParameters.at(3).toInt());
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();
1113 // 230 User logged in, proceed.
1114 emit connectState(QFtp::LoggedIn);
1115 } else if (replyCodeInt == 213) {
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();
1124 // react on new state
1127 // should never happen
1134 if (dtp.hasError()) {
1135 emit error(QFtp::UnknownError, dtp.errorMessage());
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"));
1153 emit error(QFtp::UnknownError, replyText);
1155 if (state != Waiting) {
1161 #if defined(QFTPPI_DEBUG)
1162 // qDebug("QFtpPI state: %d [processReply() end]", state);
1170 Starts next pending command. Returns false if there are no pending commands,
1171 otherwise it returns true.
1173 bool QFtpPI::startNextCmd()
1175 if (waitForDtpToConnect)
1176 // don't process any new commands until we are connected
1179 #if defined(QFTPPI_DEBUG)
1181 qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state);
1183 if (pendingCommands.isEmpty()) {
1185 emit finished(replyText);
1188 currentCmd = pendingCommands.first();
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);
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);
1213 currentCmd = QLatin1String("PORT ");
1214 currentCmd += portArg;
1216 // No IPv6 connection can be set up with the PORT
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");
1227 pendingCommands.pop_front();
1228 #if defined(QFTPPI_DEBUG)
1229 qDebug("QFtpPI send: %s", currentCmd.left(currentCmd.length()-2).toLatin1().constData());
1232 commandSocket.write(currentCmd.toLatin1());
1236 void QFtpPI::dtpConnectState(int s)
1239 case QFtpDTP::CsClosed:
1240 if (waitForDtpToClose) {
1241 // there is an unprocessed reply
1243 replyText = QLatin1String("");
1247 waitForDtpToClose = false;
1250 case QFtpDTP::CsConnected:
1251 waitForDtpToConnect = false;
1254 case QFtpDTP::CsHostNotFound:
1255 case QFtpDTP::CsConnectionRefused:
1256 emit error(QFtp::ConnectionRefused,
1257 QFtp::tr("Connection refused for data connection"));
1265 /**********************************************************************
1269 *********************************************************************/
1271 QT_BEGIN_INCLUDE_NAMESPACE
1272 #include <private/qobject_p.h>
1273 QT_END_INCLUDE_NAMESPACE
1275 class QFtpPrivate : public QObjectPrivate
1277 Q_DECLARE_PUBLIC(QFtp)
1280 inline QFtpPrivate() : close_waitForStateChange(false), state(QFtp::Unconnected),
1281 transferMode(QFtp::Passive), error(QFtp::NoError)
1284 ~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }
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&);
1293 int addCommand(QFtpCommand *cmd);
1296 QList<QFtpCommand *> pending;
1297 bool close_waitForStateChange;
1299 QFtp::TransferMode transferMode;
1301 QString errorString;
1309 int QFtpPrivate::addCommand(QFtpCommand *cmd)
1311 pending.append(cmd);
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()));
1320 /**********************************************************************
1322 * QFtp implementation
1324 *********************************************************************/
1328 \brief The QFtp class provides an implementation of the client side of FTP protocol.
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
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
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().
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.
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
1364 \snippet code/src_network_access_qftp.cpp 0
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
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
1376 Some commands, e.g. list(), emit additional signals to report
1379 Example: If you want to download the INSTALL file from the Qt
1380 FTP server, you would write this:
1382 \snippet code/src_network_access_qftp.cpp 1
1384 For this example the following sequence of signals is emitted
1385 (with small variations, depending on network traffic, etc.):
1387 \snippet code/src_network_access_qftp.cpp 2
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()
1397 If the login fails for the above example, the signals would look
1400 \snippet code/src_network_access_qftp.cpp 3
1402 You can then get details about the error with the error() and
1403 errorString() functions.
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.
1409 Call setProxy() to make QFtp connect via an FTP proxy server.
1411 The functions currentId() and currentCommand() provide more
1412 information about the currently executing command.
1414 The functions hasPendingCommands() and clearPendingCommands()
1415 allow you to query and clear the list of pending commands.
1417 If you are an experienced network programmer and want to have
1418 complete control you can use rawCommand() to execute arbitrary FTP
1421 \warning The current version of QFtp doesn't fully support
1422 non-Unix FTP servers.
1424 \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
1431 Constructs a QFtp object with the given \a parent.
1433 QFtp::QFtp(QObject *parent)
1434 : QObject(*new QFtpPrivate, parent)
1437 d->errorString = tr("Unknown error");
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)));
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)));
1460 This enum defines the connection state:
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
1471 \sa stateChanged(), state()
1475 \enum QFtp::TransferMode
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.
1482 This enum defines whether the client (Passive mode) or the server
1483 (Active mode) should set up the data connection.
1485 \value Passive The client connects to the server to transmit its
1488 \value Active The server connects to the client to transmit its
1493 \enum QFtp::TransferType
1495 This enum identifies the data transfer type used with get and
1498 \value Binary The data will be transferred in Binary mode.
1500 \value Ascii The data will be transferred in Ascii mode and new line
1501 characters will be converted to the local format.
1507 This enum identifies the error that occurred.
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
1514 \value UnknownError An error other than those specified above
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.
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.
1547 \sa currentCommand()
1552 \fn void QFtp::stateChanged(int state)
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.
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.
1562 \sa connectToHost(), close(), state(), State
1567 \fn void QFtp::listInfo(const QUrlInfo &i);
1569 This signal is emitted for each directory entry the list() command
1570 finds. The details of the entry are stored in \a i.
1577 \fn void QFtp::commandStarted(int id)
1579 This signal is emitted when processing the command identified by
1582 \sa commandFinished(), done()
1587 \fn void QFtp::commandFinished(int id, bool error)
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.
1593 \sa commandStarted(), done(), error(), errorString()
1598 \fn void QFtp::done(bool error)
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.
1605 \sa commandFinished(), error(), errorString()
1610 \fn void QFtp::readyRead()
1612 This signal is emitted in response to a get() command when there
1613 is new data to read.
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.
1619 You can read the data with the readAll() or read() functions.
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.
1626 \sa get(), read(), readAll(), bytesAvailable()
1631 \fn void QFtp::dataTransferProgress(qint64 done, qint64 total)
1633 This signal is emitted in response to a get() or put() request to
1634 indicate the current progress of the download or upload.
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).
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.
1647 \sa get(), put(), QProgressBar
1652 \fn void QFtp::rawCommandReply(int replyCode, const QString &detail);
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.
1663 Connects to the FTP server \a host using port \a port.
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.
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().
1674 When the command is started the commandStarted() signal is
1675 emitted. When it is finished the commandFinished() signal is
1678 \sa stateChanged(), commandStarted(), commandFinished()
1680 int QFtp::connectToHost(const QString &host, quint16 port)
1684 cmds << QString::number((uint)port);
1685 int id = d_func()->addCommand(new QFtpCommand(ConnectToHost, cmds));
1686 d_func()->pi.transferConnectionExtended = true;
1692 Logs in to the FTP server with the username \a user and the
1693 password \a password.
1695 The stateChanged() signal is emitted when the state of the
1696 connecting process changes, e.g. to \c LoggedIn.
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().
1703 When the command is started the commandStarted() signal is
1704 emitted. When it is finished the commandFinished() signal is
1707 \sa commandStarted(), commandFinished()
1709 int QFtp::login(const QString &user, const QString &password)
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));
1719 Closes the connection to the FTP server.
1721 The stateChanged() signal is emitted when the state of the
1722 connecting process changes, e.g. to \c Closing, then \c
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().
1730 When the command is started the commandStarted() signal is
1731 emitted. When it is finished the commandFinished() signal is
1734 \sa stateChanged(), commandStarted(), commandFinished()
1738 return d_func()->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n"))));
1743 Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.
1745 \sa QFtp::TransferMode
1747 int QFtp::setTransferMode(TransferMode mode)
1749 int id = d_func()->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
1750 d_func()->pi.transferConnectionExtended = true;
1751 d_func()->transferMode = mode;
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.
1760 QFtp does not support FTP-over-HTTP proxy servers. Use
1761 QNetworkAccessManager for this.
1763 int QFtp::setProxy(const QString &host, quint16 port)
1766 args << host << QString::number(port);
1767 return d_func()->addCommand(new QFtpCommand(SetProxy, args));
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.
1775 The listInfo() signal is emitted for each directory entry found.
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().
1782 When the command is started the commandStarted() signal is
1783 emitted. When it is finished the commandFinished() signal is
1786 \sa listInfo(), commandStarted(), commandFinished()
1788 int QFtp::list(const QString &dir)
1791 cmds << QLatin1String("TYPE A\r\n");
1792 cmds << QLatin1String(d_func()->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");
1794 cmds << QLatin1String("LIST\r\n");
1796 cmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));
1797 return d_func()->addCommand(new QFtpCommand(List, cmds));
1802 Changes the working directory of the server to \a dir.
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().
1809 When the command is started the commandStarted() signal is
1810 emitted. When it is finished the commandFinished() signal is
1813 \sa commandStarted(), commandFinished()
1815 int QFtp::cd(const QString &dir)
1817 return d_func()->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n"))));
1822 Downloads the file \a file from the server.
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.
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.
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.
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
1846 The data is transferred as Binary or Ascii depending on the value
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().
1854 When the command is started the commandStarted() signal is
1855 emitted. When it is finished the commandFinished() signal is
1858 \sa readyRead(), dataTransferProgress(), commandStarted(),
1861 int QFtp::get(const QString &file, QIODevice *dev, TransferType type)
1865 cmds << QLatin1String("TYPE I\r\n");
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));
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.
1882 The data is transferred as Binary or Ascii depending on the value
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().
1890 When the command is started the commandStarted() signal is
1891 emitted. When it is finished the commandFinished() signal is
1894 Since this function takes a copy of the \a data, you can discard
1895 your own copy when this function returns.
1897 \sa dataTransferProgress(), commandStarted(), commandFinished()
1899 int QFtp::put(const QByteArray &data, const QString &file, TransferType type)
1903 cmds << QLatin1String("TYPE I\r\n");
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));
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
1920 The data is transferred as Binary or Ascii depending on the value
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
1927 int QFtp::put(QIODevice *dev, const QString &file, TransferType type)
1931 cmds << QLatin1String("TYPE I\r\n");
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));
1943 Deletes the file called \a file from the server.
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().
1950 When the command is started the commandStarted() signal is
1951 emitted. When it is finished the commandFinished() signal is
1954 \sa commandStarted(), commandFinished()
1956 int QFtp::remove(const QString &file)
1958 return d_func()->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n"))));
1963 Creates a directory called \a dir on the server.
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().
1970 When the command is started the commandStarted() signal is
1971 emitted. When it is finished the commandFinished() signal is
1974 \sa commandStarted(), commandFinished()
1976 int QFtp::mkdir(const QString &dir)
1978 return d_func()->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n"))));
1983 Removes the directory called \a dir from the server.
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().
1990 When the command is started the commandStarted() signal is
1991 emitted. When it is finished the commandFinished() signal is
1994 \sa commandStarted(), commandFinished()
1996 int QFtp::rmdir(const QString &dir)
1998 return d_func()->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n"))));
2003 Renames the file called \a oldname to \a newname on the server.
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().
2010 When the command is started the commandStarted() signal is
2011 emitted. When it is finished the commandFinished() signal is
2014 \sa commandStarted(), commandFinished()
2016 int QFtp::rename(const QString &oldname, const QString &newname)
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));
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
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().
2037 When the command is started the commandStarted() signal is
2038 emitted. When it is finished the commandFinished() signal is
2041 \sa rawCommandReply(), commandStarted(), commandFinished()
2043 int QFtp::rawCommand(const QString &command)
2045 QString cmd = command.trimmed() + QLatin1String("\r\n");
2046 return d_func()->addCommand(new QFtpCommand(RawCommand, QStringList(cmd)));
2051 Returns the number of bytes that can be read from the data socket
2054 \sa get(), readyRead(), read(), readAll()
2056 qint64 QFtp::bytesAvailable() const
2058 return d_func()->pi.dtp.bytesAvailable();
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.
2066 \sa get(), readyRead(), bytesAvailable(), readAll()
2068 qint64 QFtp::read(char *data, qint64 maxlen)
2070 return d_func()->pi.dtp.read(data, maxlen);
2075 Reads all the bytes available from the data socket and returns
2078 \sa get(), readyRead(), bytesAvailable(), read()
2080 QByteArray QFtp::readAll()
2082 return d_func()->pi.dtp.readAll();
2087 Aborts the current command and deletes all scheduled commands.
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.
2100 For all other commands that are affected by the abort(), no
2101 signals are emitted.
2103 If you don't start further FTP commands directly after the
2104 abort(), there won't be any scheduled commands and the done()
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.
2113 \sa clearPendingCommands()
2117 if (d_func()->pending.isEmpty())
2120 clearPendingCommands();
2121 d_func()->pi.abort();
2126 Returns the identifier of the FTP command that is being executed
2127 or 0 if there is no command being executed.
2129 \sa currentCommand()
2131 int QFtp::currentId() const
2133 if (d_func()->pending.isEmpty())
2135 return d_func()->pending.first()->id;
2140 Returns the command type of the FTP command being executed or \c
2141 None if there is no command being executed.
2145 QFtp::Command QFtp::currentCommand() const
2147 if (d_func()->pending.isEmpty())
2149 return d_func()->pending.first()->command;
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.
2158 This function can be used to delete the QIODevice in the slot connected to
2159 the commandFinished() signal.
2163 QIODevice* QFtp::currentDevice() const
2165 if (d_func()->pending.isEmpty())
2167 QFtpCommand *c = d_func()->pending.first();
2175 Returns true if there are any commands scheduled that have not yet
2176 been executed; otherwise returns false.
2178 The command that is being executed is \e not considered as a
2181 \sa clearPendingCommands(), currentId(), currentCommand()
2183 bool QFtp::hasPendingCommands() const
2185 return d_func()->pending.count() > 1;
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().
2194 \sa hasPendingCommands(), abort()
2196 void QFtp::clearPendingCommands()
2198 // delete all entires except the first one
2199 while (d_func()->pending.count() > 1)
2200 delete d_func()->pending.takeLast();
2205 Returns the current state of the object. When the state changes,
2206 the stateChanged() signal is emitted.
2208 \sa State, stateChanged()
2210 QFtp::State QFtp::state() const
2212 return d_func()->state;
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.
2221 If you start a new command, the error status is reset to \c NoError.
2223 QFtp::Error QFtp::error() const
2225 return d_func()->error;
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.
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
2240 QString QFtp::errorString() const
2242 return d_func()->errorString;
2247 void QFtpPrivate::_q_startNextCommand()
2250 if (pending.isEmpty())
2252 QFtpCommand *c = pending.first();
2254 error = QFtp::NoError;
2255 errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));
2257 if (q->bytesAvailable())
2258 q->readAll(); // clear the data
2259 emit q->commandStarted(c->id);
2261 // Proxy support, replace the Login argument in place, then fall
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;
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();
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"));
2284 if (!proxyHost.isEmpty()) {
2285 host = c->rawCmds[0];
2286 port = c->rawCmds[1].toUInt();
2287 pi.connectToHost(proxyHost, proxyPort);
2289 pi.connectToHost(c->rawCmds[0], c->rawCmds[1].toUInt());
2292 if (c->command == QFtp::Put) {
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()));
2303 pi.dtp.setBytesTotal(c->data.dev->size());
2306 } else if (c->command == QFtp::Get) {
2307 if (!c->is_ba && c->data.dev) {
2308 pi.dtp.setDevice(c->data.dev);
2310 } else if (c->command == QFtp::Close) {
2311 state = QFtp::Closing;
2312 emit q->stateChanged(state);
2314 pi.sendCommands(c->rawCmds);
2320 void QFtpPrivate::_q_piFinished(const QString&)
2322 if (pending.isEmpty())
2324 QFtpCommand *c = pending.first();
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()
2331 if (state != QFtp::Unconnected) {
2332 close_waitForStateChange = true;
2336 emit q_func()->commandFinished(c->id, false);
2337 pending.removeFirst();
2341 if (pending.isEmpty()) {
2342 emit q_func()->done(false);
2344 _q_startNextCommand();
2350 void QFtpPrivate::_q_piError(int errorCode, const QString &text)
2354 if (pending.isEmpty()) {
2355 qWarning("QFtpPrivate::_q_piError was called without pending command!");
2359 QFtpCommand *c = pending.first();
2362 if (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {
2363 pi.dtp.setBytesTotal(0);
2365 } else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {
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"))
2376 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1"))
2380 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1"))
2384 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1"))
2388 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1"))
2392 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1"))
2396 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1"))
2400 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1"))
2404 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1"))
2412 pi.clearPendingCommands();
2413 q->clearPendingCommands();
2414 emit q->commandFinished(c->id, true);
2416 pending.removeFirst();
2418 if (pending.isEmpty())
2421 _q_startNextCommand();
2426 void QFtpPrivate::_q_piConnectState(int connectState)
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")));
2438 void QFtpPrivate::_q_piFtpReply(int code, const QString &text)
2440 if (q_func()->currentCommand() == QFtp::RawCommand) {
2441 pi.rawCommand = true;
2442 emit q_func()->rawCommandReply(code, text);
2460 #include "moc_qftp_p.cpp"