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