📄 qftp.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtNetwork module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************///#define QFTPPI_DEBUG//#define QFTPDTP_DEBUG#include "qftp.h"#include "qabstractsocket.h"#ifndef QT_NO_FTP#include "qcoreapplication.h"#include "qtcpsocket.h"#include "qurlinfo.h"#include "qstringlist.h"#include "qregexp.h"#include "qtimer.h"#include "qfileinfo.h"#include "qhash.h"#include "qtcpserver.h"class QFtpPI;/* The QFtpDTP (DTP = Data Transfer Process) controls all client side data transfer between the client and server.*/class QFtpDTP : public QObject{ Q_OBJECTpublic: enum ConnectState { CsHostFound, CsConnected, CsClosed, CsHostNotFound, CsConnectionRefused }; QFtpDTP(QFtpPI *p, QObject *parent = 0); void setData(QByteArray *); void setDevice(QIODevice *); void writeData(); void setBytesTotal(qint64 bytes); bool hasError() const; QString errorMessage() const; void clearError(); void connectToHost(const QString & host, quint16 port); int setupListener(const QHostAddress &address); void waitForConnection(); QTcpSocket::SocketState state() const; qint64 bytesAvailable() const; qint64 read(char *data, qint64 maxlen); QByteArray readAll(); void abortConnection(); static bool parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info);signals: void listInfo(const QUrlInfo&); void readyRead(); void dataTransferProgress(qint64, qint64); void connectState(int);private slots: void socketConnected(); void socketReadyRead(); void socketError(QAbstractSocket::SocketError); void socketConnectionClosed(); void socketBytesWritten(qint64); void setupSocket();private: void clearData(); QTcpSocket *socket; QTcpServer listener; QFtpPI *pi; QString err; qint64 bytesDone; qint64 bytesTotal; bool callWriteData; // If is_ba is true, ba is used; ba is never 0. // Otherwise dev is used; dev can be 0 or not. union { QByteArray *ba; QIODevice *dev; } data; bool is_ba; QByteArray bytesFromSocket;};/********************************************************************** * * QFtpPI - Protocol Interpreter * *********************************************************************/class QFtpPI : public QObject{ Q_OBJECTpublic: QFtpPI(QObject *parent = 0); void connectToHost(const QString &host, quint16 port); bool sendCommands(const QStringList &cmds); bool sendCommand(const QString &cmd) { return sendCommands(QStringList(cmd)); } void clearPendingCommands(); void abort(); QString currentCommand() const { return currentCmd; } bool rawCommand; bool transferConnectionExtended; QFtpDTP dtp; // the PI has a DTP which is not the design of RFC 959, but it // makes the design simpler this waysignals: void connectState(int); void finished(const QString&); void error(int, const QString&); void rawFtpReply(int, const QString&);private slots: void hostFound(); void connected(); void connectionClosed(); void delayedCloseFinished(); void readyRead(); void error(QAbstractSocket::SocketError); void dtpConnectState(int);private: // the states are modelled after the generalized state diagram of RFC 959, // page 58 enum State { Begin, Idle, Waiting, Success, Failure }; enum AbortState { None, AbortStarted, WaitForAbortToFinish }; bool processReply(); bool startNextCmd(); QTcpSocket commandSocket; QString replyText; char replyCode[3]; State state; AbortState abortState; QStringList pendingCommands; QString currentCmd; bool waitForDtpToConnect; bool waitForDtpToClose; QByteArray bytesFromSocket; friend class QFtpDTP;};/********************************************************************** * * QFtpCommand implemenatation * *********************************************************************/class QFtpCommand{public: QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba); QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev = 0); ~QFtpCommand(); int id; QFtp::Command command; QStringList rawCmds; // If is_ba is true, ba is used; ba is never 0. // Otherwise dev is used; dev can be 0 or not. union { QByteArray *ba; QIODevice *dev; } data; bool is_ba; static QBasicAtomic idCounter;};QBasicAtomic QFtpCommand::idCounter = Q_ATOMIC_INIT(1);QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba) : command(cmd), rawCmds(raw), is_ba(true){ id = idCounter.fetchAndAdd(1); data.ba = new QByteArray(ba);}QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev) : command(cmd), rawCmds(raw), is_ba(false){ id = idCounter.fetchAndAdd(1); data.dev = dev;}QFtpCommand::~QFtpCommand(){ if (is_ba) delete data.ba;}/********************************************************************** * * QFtpDTP implemenatation * *********************************************************************/QFtpDTP::QFtpDTP(QFtpPI *p, QObject *parent) : QObject(parent), socket(0), listener(this), pi(p), callWriteData(false){ clearData(); listener.setObjectName(QLatin1String("QFtpDTP active state server")); connect(&listener, SIGNAL(newConnection()), SLOT(setupSocket()));}void QFtpDTP::setData(QByteArray *ba){ is_ba = true; data.ba = ba;}void QFtpDTP::setDevice(QIODevice *dev){ is_ba = false; data.dev = dev;}void QFtpDTP::setBytesTotal(qint64 bytes){ bytesTotal = bytes; bytesDone = 0; emit dataTransferProgress(bytesDone, bytesTotal);}void QFtpDTP::connectToHost(const QString & host, quint16 port){ bytesFromSocket.clear(); if (socket) delete socket; socket = new QTcpSocket(this); socket->setObjectName(QLatin1String("QFtpDTP Passive state socket")); connect(socket, SIGNAL(connected()), SLOT(socketConnected())); connect(socket, SIGNAL(readyRead()), SLOT(socketReadyRead())); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError))); connect(socket, SIGNAL(disconnected()), SLOT(socketConnectionClosed())); connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64))); socket->connectToHost(host, port);}int QFtpDTP::setupListener(const QHostAddress &address){ if (!listener.isListening() && !listener.listen(address, 0)) return -1; return listener.serverPort();}void QFtpDTP::waitForConnection(){ // This function is only interesting in Active transfer mode; it works // around a limitation in QFtp's design by blocking, waiting for an // incoming connection. For the default Passive mode, it does nothing. if (listener.isListening()) listener.waitForNewConnection();}QTcpSocket::SocketState QFtpDTP::state() const{ return socket ? socket->state() : QTcpSocket::UnconnectedState;}qint64 QFtpDTP::bytesAvailable() const{ if (!socket || socket->state() != QTcpSocket::ConnectedState) return (qint64) bytesFromSocket.size(); return socket->bytesAvailable();}qint64 QFtpDTP::read(char *data, qint64 maxlen){ qint64 read; if (socket && socket->state() == QTcpSocket::ConnectedState) { read = socket->read(data, maxlen); } else { read = bytesFromSocket.size(); memcpy(data, bytesFromSocket.data(), read); bytesFromSocket.clear(); } bytesDone += read; return read;}QByteArray QFtpDTP::readAll(){ QByteArray tmp; if (socket && socket->state() == QTcpSocket::ConnectedState) { tmp = socket->readAll(); bytesDone += tmp.size(); } else { tmp = bytesFromSocket; bytesFromSocket.clear(); } return tmp;}void QFtpDTP::writeData(){ if (!socket) return; if (is_ba) {#if defined(QFTPDTP_DEBUG) qDebug("QFtpDTP::writeData: write %d bytes", data.ba->size());#endif if (data.ba->size() == 0) emit dataTransferProgress(0, bytesTotal); else socket->write(data.ba->data(), data.ba->size()); socket->close(); clearData(); } else if (data.dev) { callWriteData = false; const qint64 blockSize = 16*1024; char buf[16*1024]; while (!data.dev->atEnd() && socket->bytesToWrite() == 0) { qint64 read = data.dev->read(buf, blockSize);#if defined(QFTPDTP_DEBUG) qDebug("QFtpDTP::writeData: write() of size %lli bytes", read);#endif socket->write(buf, read); if (!data.dev) return; // this can happen when a command is aborted } if (data.dev->atEnd()) { if (bytesDone == 0 && socket->bytesToWrite() == 0) emit dataTransferProgress(0, bytesTotal); socket->close();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -