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

Last change on this file since 82 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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