source: trunk/src/network/access/qftp.cpp@ 890

Last change on this file since 890 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 75.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
61QT_BEGIN_NAMESPACE
62
63class QFtpPI;
64
65/*
66 The QFtpDTP (DTP = Data Transfer Process) controls all client side
67 data transfer between the client and server.
68*/
69class QFtpDTP : public QObject
70{
71 Q_OBJECT
72
73public:
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
106signals:
107 void listInfo(const QUrlInfo&);
108 void readyRead();
109 void dataTransferProgress(qint64, qint64);
110
111 void connectState(int);
112
113private 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
123private:
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
152class QFtpPI : public QObject
153{
154 Q_OBJECT
155
156public:
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
176signals:
177 void connectState(int);
178 void finished(const QString&);
179 void error(int, const QString&);
180 void rawFtpReply(int, const QString&);
181
182private 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
192private:
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 *********************************************************************/
233class QFtpCommand
234{
235public:
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
255QBasicAtomicInt QFtpCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
256
257QFtpCommand::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
264QFtpCommand::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
271QFtpCommand::~QFtpCommand()
272{
273 if (is_ba)
274 delete data.ba;
275}
276
277/**********************************************************************
278 *
279 * QFtpDTP implemenatation
280 *
281 *********************************************************************/
282QFtpDTP::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
294void QFtpDTP::setData(QByteArray *ba)
295{
296 is_ba = true;
297 data.ba = ba;
298}
299
300void QFtpDTP::setDevice(QIODevice *dev)
301{
302 is_ba = false;
303 data.dev = dev;
304}
305
306void QFtpDTP::setBytesTotal(qint64 bytes)
307{
308 bytesTotal = bytes;
309 bytesDone = 0;
310 emit dataTransferProgress(bytesDone, bytesTotal);
311}
312
313void 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
332int QFtpDTP::setupListener(const QHostAddress &address)
333{
334 if (!listener.isListening() && !listener.listen(address, 0))
335 return -1;
336 return listener.serverPort();
337}
338
339void 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
348QTcpSocket::SocketState QFtpDTP::state() const
349{
350 return socket ? socket->state() : QTcpSocket::UnconnectedState;
351}
352
353qint64 QFtpDTP::bytesAvailable() const
354{
355 if (!socket || socket->state() != QTcpSocket::ConnectedState)
356 return (qint64) bytesFromSocket.size();
357 return socket->bytesAvailable();
358}
359
360qint64 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
375QByteArray 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
388void 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
428void QFtpDTP::dataReadyRead()
429{
430 writeData();
431}
432
433inline bool QFtpDTP::hasError() const
434{
435 return !err.isNull();
436}
437
438inline QString QFtpDTP::errorMessage() const
439{
440 return err;
441}
442
443inline void QFtpDTP::clearError()
444{
445 err.clear();
446}
447
448void 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
461static 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
472static 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
557static 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
610bool 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
637void 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
646void 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
718void 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
733void 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
746void 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
757void 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
770void QFtpDTP::clearData()
771{
772 is_ba = false;
773 data.dev = 0;
774}
775
776/**********************************************************************
777 *
778 * QFtpPI implemenatation
779 *
780 *********************************************************************/
781QFtpPI::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
808void 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*/
822bool 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
837void QFtpPI::clearPendingCommands()
838{
839 pendingCommands.clear();
840 dtp.abortConnection();
841 currentCmd.clear();
842 state = Idle;
843}
844
845void 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
863void QFtpPI::hostFound()
864{
865 emit connectState(QFtp::Connecting);
866}
867
868void 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
880void QFtpPI::connectionClosed()
881{
882 commandSocket.close();
883 emit connectState(QFtp::Unconnected);
884}
885
886void QFtpPI::delayedCloseFinished()
887{
888 emit connectState(QFtp::Unconnected);
889}
890
891void 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
908void 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*/
965bool 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*/
1143bool 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
1206void 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
1241QT_BEGIN_INCLUDE_NAMESPACE
1242#include <private/qobject_p.h>
1243QT_END_INCLUDE_NAMESPACE
1244
1245class QFtpPrivate : public QObjectPrivate
1246{
1247 Q_DECLARE_PUBLIC(QFtp)
1248public:
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
1279int 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*/
1401QFtp::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*/
1429QFtp::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*/
1664int 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*/
1692int 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*/
1718int 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*/
1728int 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*/
1743int 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*/
1767int 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*/
1793int 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*/
1838int 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*/
1875int 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*/
1902int 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*/
1930int 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*/
1949int 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*/
1968int 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*/
1987int 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*/
2013int 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*/
2025qint64 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*/
2041qint64 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*/
2052QByteArray 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*/
2086void 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*/
2101int 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*/
2114QFtp::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*/
2131QIODevice* 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*/
2150bool 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*/
2162void 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*/
2175QFtp::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*/
2187QFtp::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*/
2203QString QFtp::errorString() const
2204{
2205 return d_func()->errorString;
2206}
2207
2208/*! \internal
2209*/
2210void 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*/
2279void 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*/
2309void QFtpPrivate::_q_piError(int errorCode, const QString &text)
2310{
2311 Q_Q(QFtp);
2312
2313 if (pending.isEmpty()) {
2314 qWarning("QFtpPrivate::_q_piError was called without pending command!");
2315 return;
2316 }
2317
2318 QFtpCommand *c = pending.first();
2319
2320 // non-fatal errors
2321 if (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {
2322 pi.dtp.setBytesTotal(-1);
2323 return;
2324 } else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {
2325 return;
2326 }
2327
2328 error = QFtp::Error(errorCode);
2329 switch (q->currentCommand()) {
2330 case QFtp::ConnectToHost:
2331 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1"))
2332 .arg(text);
2333 break;
2334 case QFtp::Login:
2335 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1"))
2336 .arg(text);
2337 break;
2338 case QFtp::List:
2339 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1"))
2340 .arg(text);
2341 break;
2342 case QFtp::Cd:
2343 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1"))
2344 .arg(text);
2345 break;
2346 case QFtp::Get:
2347 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1"))
2348 .arg(text);
2349 break;
2350 case QFtp::Put:
2351 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1"))
2352 .arg(text);
2353 break;
2354 case QFtp::Remove:
2355 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1"))
2356 .arg(text);
2357 break;
2358 case QFtp::Mkdir:
2359 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1"))
2360 .arg(text);
2361 break;
2362 case QFtp::Rmdir:
2363 errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1"))
2364 .arg(text);
2365 break;
2366 default:
2367 errorString = text;
2368 break;
2369 }
2370
2371 pi.clearPendingCommands();
2372 q->clearPendingCommands();
2373 emit q->commandFinished(c->id, true);
2374
2375 pending.removeFirst();
2376 delete c;
2377 if (pending.isEmpty())
2378 emit q->done(true);
2379 else
2380 _q_startNextCommand();
2381}
2382
2383/*! \internal
2384*/
2385void QFtpPrivate::_q_piConnectState(int connectState)
2386{
2387 state = QFtp::State(connectState);
2388 emit q_func()->stateChanged(state);
2389 if (close_waitForStateChange) {
2390 close_waitForStateChange = false;
2391 _q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));
2392 }
2393}
2394
2395/*! \internal
2396*/
2397void QFtpPrivate::_q_piFtpReply(int code, const QString &text)
2398{
2399 if (q_func()->currentCommand() == QFtp::RawCommand) {
2400 pi.rawCommand = true;
2401 emit q_func()->rawCommandReply(code, text);
2402 }
2403}
2404
2405/*!
2406 Destructor.
2407*/
2408QFtp::~QFtp()
2409{
2410 abort();
2411 close();
2412}
2413
2414QT_END_NAMESPACE
2415
2416#include "qftp.moc"
2417
2418#include "moc_qftp.cpp"
2419
2420#endif // QT_NO_FTP
Note: See TracBrowser for help on using the repository browser.