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