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