Qt网络编程例子
目錄
Qt網絡編程
1.TCP編程例子
2.UDP編程例子
3.HTTP例子
4.FTP例子
Qt各個版本的下載地址:http://download.qt.io/archive/qt/
在Windows上與VS配合使用的Qt的編譯器用的是MSVC有個問題:在QtCreator中寫的代碼對字符串老報錯,對tr()里面的字符串也是這樣。
所以如果只是在Windows上純用Qt的話,就考慮用MinGW版本的編譯器。
MinGW:Minimalist GNU for Windows是一套Windows上的GNU工具集,用其開發的Qt程序不需要第三方dll支持的情況下就可以直接在Windows上運行!(參考:http://www.mingw.org/)
要對套接字的底層有更多的了解與編程控制,就要去看伯克利套接字和WinSock。
這兩個套接字編程的層次更接近于操作系統的系統功能調用,I/O模型,線程同步控制等。
?
高層應用:如果只是普通應用程序編程,對于Winsock有MFC的封裝。
也可以用Qt的網絡編程模塊。
?
Network Programming with Qt
Qt中的Qt Network模塊用來編寫基于TCP/IP的網絡應用程序。
(SSL(Secure Sockets Layer 安全套接層),及其繼任者傳輸層安全(Transport Layer Security,TLS)是為網絡通信提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡連接進行加密。)
(SSL協議位于TCP/IP協議與各種應用層協議之間,為數據通訊提供安全支持。SSL協議可分為兩層: SSL記錄協議(SSLRecord Protocol):它建立在可靠的傳輸協議(如TCP)之上,為高層協議提供數據封裝、壓縮、加密等基本功能的支持。 SSL握手協議(SSL Handshake Protocol):它建立在SSL記錄協議之上,用于在實際的數據傳輸開始前,通訊雙方進行身份認證、協商加密算法、交換加密密鑰等。)
1. UDP (QUdpSocket)
UDP(User Datagram Protocol,用戶數據報協議)輕量級、不可靠、面向數據報、無連接的傳輸層協議。
下面是一個用QUdpSocket廣播IPv4數據報的例子。
廣播一般用來實現網絡發現協議,例如,查找網絡上哪臺主機擁有最多的硬盤空間,該主機就向網絡中廣播一個數據報,然后所以收到該數據報的主機,就會把自己的硬盤空間信息以應答包的方式發回給請求端。發送端(請求端)一直等待,直到它接受到所有主機的應答,然后選擇擁有最多空閑磁盤空間的主機服務器來存儲數據。
要廣播一個數據報,只需要把數據報發送到一個特殊的地址QHostAddress::Broadcast(255.255.255.255),或者是本網絡的廣播地址。
用QUdpSocket實現發生廣播數據報:可以運行多個接受端,只有IP地址和端口號設置對,就可以將發生者的廣播信息發生出去,接收者接受廣播信息。
UDP發送和接收都不需要建立連接,只需要綁定好地址與端口后就可以發送和接收數據了。發送端直接發送,接受端只需要循環判斷數據接收完了沒有,沒接收完就循環接收就OK。
Qt中信號與槽的機制很好的實現了收到數據發送信號觸發處理函數。類似與WinSock里面的異步模型,需要事件對象來對這一流程做同步的情況。面向界面GUI的時候,這一信號與槽機制比MFC里的一大堆線程同步類好用。
UDP發送端:
udpsender:
sender.h:
#ifndef SENDER_H #define SENDER_H#include <QDialog> class QUdpSocket;namespace Ui { class Sender; }class Sender : public QDialog {Q_OBJECTpublic:explicit Sender(QWidget *parent = 0);~Sender();private slots:void on_pushButton_clicked();private:Ui::Sender *ui;QUdpSocket *sender; };#endif // SENDER_Hsender.cpp
#include "sender.h" #include "ui_sender.h" #include <QtNetwork>Sender::Sender(QWidget *parent) :QDialog(parent),ui(new Ui::Sender) {ui->setupUi(this);sender = new QUdpSocket(this); }Sender::~Sender() {delete ui; }void Sender::on_pushButton_clicked() {QByteArray datagram = "hello world!";//調用writeDatagram函數來發送數據sender->writeDatagram(datagram.data(), datagram.size(),QHostAddress::Broadcast, 45454);}udpreceiver:
receiver.h
#ifndef RECEIVER_H #define RECEIVER_H#include <QDialog> class QUdpSocket; namespace Ui { class Receiver; }class Receiver : public QDialog {Q_OBJECTpublic:explicit Receiver(QWidget *parent = 0);~Receiver();private:Ui::Receiver *ui;QUdpSocket *receiver;private slots:void processPendingDatagram();};#endif // RECEIVER_Hreceiver.cpp
#include "receiver.h" #include "ui_receiver.h" #include <QtNetwork>Receiver::Receiver(QWidget *parent) :QDialog(parent),ui(new Ui::Receiver) {ui->setupUi(this);receiver = new QUdpSocket(this);receiver->bind(45454, QUdpSocket::ShareAddress);connect(receiver, &QUdpSocket::readyRead, this, &Receiver::processPendingDatagram);}Receiver::~Receiver() {delete ui; }void Receiver::processPendingDatagram() {// 擁有等待的數據報while(receiver->hasPendingDatagrams()){QByteArray datagram;// 讓datagram的大小為等待處理的數據報的大小,這樣才能接收到完整的數據datagram.resize(receiver->pendingDatagramSize());// 接收數據報,將其存放到datagram中receiver->readDatagram(datagram.data(), datagram.size());ui->label->setText(datagram);} }2. TCP(QTcpSocket、QTcpServe)
TCP(Transmission Control Protocol,傳輸控制協議)一個用于數據傳輸的底層網絡協議。(Http、FTP都是基于TCP)
TCP面向數據流、連接的可靠傳輸層協議。
可以用QTcpSocket來實現POP3、SMTP、NNTP等標準網絡協議。
QTcpSocket傳輸的是連續的數據流,適合連續傳輸數據。
TCP:C/S模型,在數據傳輸之前、必須在C/S直接建立TCP連接。
下面的例子實現:服務器端一直監聽端口,一旦有客戶端連接請求到達,便建立連接,連接建立好后向客戶端發送一個字符串,客戶端收到該字符串并顯示出來。
服務器端:
server.h
#ifndef SERVER_H #define SERVER_H#include <QDialog> class QTcpServer;namespace Ui { class Server; }class Server : public QDialog {Q_OBJECTpublic:explicit Server(QWidget *parent = 0);~Server();private:Ui::Server *ui;QTcpServer *tcpServer;private slots:void sendMessage();};#endif // SERVER_Hserver.cpp
#include "server.h" #include "ui_server.h" #include <QtNetwork>Server::Server(QWidget *parent) :QDialog(parent),ui(new Ui::Server) {ui->setupUi(this);tcpServer = new QTcpServer(this);// 使用了IPv4的本地主機地址,等價于QHostAddress("127.0.0.1")if (!tcpServer->listen(QHostAddress::LocalHost, 6666)) {qDebug() << tcpServer->errorString();close();}connect(tcpServer, &QTcpServer::newConnection,this, &Server::sendMessage);}Server::~Server() {delete ui; }void Server::sendMessage() {// 用于暫存要發送的數據QByteArray block;QDataStream out(&block, QIODevice::WriteOnly);// 設置數據流的版本,客戶端和服務器端使用的版本要相同out.setVersion(QDataStream::Qt_5_6);out << (quint16)0;out << tr("hello TCP!!!");out.device()->seek(0);out << (quint16)(block.size() - sizeof(quint16));// 獲取已經建立的連接的套接字QTcpSocket *clientConnection = tcpServer->nextPendingConnection();connect(clientConnection, &QTcpSocket::disconnected,clientConnection, &QTcpSocket::deleteLater);clientConnection->write(block);clientConnection->disconnectFromHost();// 發送數據成功后,顯示提示ui->label->setText(tr("發送數據成功!!!")); }客戶端:
client.h
#ifndef CLIENT_H #define CLIENT_H#include <QDialog> #include <QAbstractSocket> class QTcpSocket;namespace Ui { class Client; }class Client : public QDialog {Q_OBJECTpublic:explicit Client(QWidget *parent = 0);~Client();private:Ui::Client *ui;QTcpSocket *tcpSocket;QString message;// 用來存放數據的大小信息quint16 blockSize;private slots:void newConnect();void readMessage();void displayError(QAbstractSocket::SocketError);void on_connectButton_clicked(); };#endif // CLIENT_Hclient.cpp
#include "client.h" #include "ui_client.h" #include <QtNetwork>Client::Client(QWidget *parent) :QDialog(parent),ui(new Ui::Client) {ui->setupUi(this);tcpSocket = new QTcpSocket(this);connect(tcpSocket, &QTcpSocket::readyRead, this, &Client::readMessage);connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(displayError(QAbstractSocket::SocketError)));}Client::~Client() {delete ui; }void Client::newConnect() {// 初始化數據大小信息為0blockSize = 0;// 取消已有的連接tcpSocket->abort();tcpSocket->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt()); }void Client::readMessage() {QDataStream in(tcpSocket);// 設置數據流版本,這里要和服務器端相同in.setVersion(QDataStream::Qt_5_6);// 如果是剛開始接收數據if (blockSize == 0) {//判斷接收的數據是否大于兩字節,也就是文件的大小信息所占的空間//如果是則保存到blockSize變量中,否則直接返回,繼續接收數據if(tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return;in >> blockSize;}// 如果沒有得到全部的數據,則返回,繼續接收數據if(tcpSocket->bytesAvailable() < blockSize) return;// 將接收到的數據存放到變量中in >> message;// 顯示接收到的數據ui->messageLabel->setText(message); }void Client::displayError(QAbstractSocket::SocketError) {qDebug() << tcpSocket->errorString(); }void Client::on_connectButton_clicked() {newConnect(); }3.HTTP
HTTP(HyperText Transfer Protocol,超文本傳輸協議)是一個客戶端和服務器端之間進行請求和應答的標準。
HTTP的C/S交互過程:
通常由HTTP客戶端發起一個請求,建立一個到服務器指定端口(默認是80端口)的TCP連接;
HTTP服務器在指定端口(80端口)監聽客戶端發過來的請求,一旦收到請求,服務器端就會向客戶端發回一個應答。
在Qt中,高層協議的應用就是用好這三個類:QNetworkAccessManager、QNetworkRequest、QNetworkReply;
The QNetworkAccessManager class allows the application to send network requests and receive replies.
所以網絡請求的發送和網絡應答的接收都是通過QNetworkAccessManager類調用相關的方法來完成了。
調用get()方法發送一個網絡請求。得到一個網頁page數據。
QNetworkReply *get(const QNetworkRequest& request)QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
Posts a request to obtain the contents of the target request and returns a new QNetworkReply object opened for reading which emits the readyRead() signal whenever new data arrives.
The contents as well as associated headers will be downloaded.
See also post(), put(), deleteResource(), and sendCustomRequest().
Once a QNetworkAccessManager object has been created, the application can use it to send requests over the network. A group of standard functions are supplied that take a request and optional data, and each return a QNetworkReply object. The returned object is used to obtain any data returned in response to the corresponding request.
QNetworkRequest:
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
QNetworkRequest is part of the Network Access API and is the class holding the information necessary to send a request over the network. It contains a URL and some ancillary information that can be used to modify the request.
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager
The QNetworkReply class contains the data and meta data related to a request posted with QNetworkAccessManager. Like QNetworkRequest, it contains a URL and headers (both in parsed and raw form), some information about the reply's state and the contents of the reply itself.
QNetworkReply is a sequential-access QIODevice, which means that once data is read from the object, it no longer kept by the device. It is therefore the application's responsibility to keep this data if it needs to. Whenever more data is received from the network and processed, the readyRead() signal is emitted.
The downloadProgress() signal is also emitted when data is received, but the number of bytes contained in it may not represent the actual bytes received, if any transformation is done to the contents (for example, decompressing and removing the protocol overhead).
Even though QNetworkReply is a QIODevice connected to the contents of the reply, it also emits the uploadProgress() signal, which indicates the progress of the upload for operations that have such content.
Note: Do not delete the object in the slot connected to the error() or finished() signal. Use deleteLater().
因為QNetworkReply繼承自QIODevice類,所以可以像操作一般的I/O設備一樣操作該類。
下面的例子使用了readAll()函數來讀取所有的應答數據,完成數據的讀取后需要使用deleteLater()來刪除reply對象。上面的Note中已經指出,不能在與QNetworkReply對象的完成信號和錯誤信號關聯的槽函數中delete調reply對象,應該調用deleteLater()滯后刪除該對象。
下面的代碼通過網絡訪問接口實現HTTP通信的例子:
網絡請求使用QNetworkRequset類表示,get()函數返回一個QNetworkReply對象
將返回的網頁以文本的方式顯示:
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QNetworkReply; class QNetworkAccessManager;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private:Ui::MainWindow *ui;QNetworkAccessManager *manager;private slots:void replyFinished(QNetworkReply *);};#endif // MAINWINDOW_Hmainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtNetwork>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);manager = new QNetworkAccessManager(this);/*這里首先創建了一個QNetworkAccessManager類的實例,用它來發送網絡請求和接受應答*//*將QNetworkAccessManager的finished信號和自定義的槽函數replyFinished相關聯,* 每當網絡請求結束時,都會收到finished信號并觸發執行槽函數replyFinished*/connect(manager, &QNetworkAccessManager::finished,this, &MainWindow::replyFinished);manager->get(QNetworkRequest(QUrl("http://www.yangbo.pro")));/*用get方法發送一個網絡請求,網絡請求使用QNetworkRequest()表示,* get()函數返回一個QNetworkReply對象信號函數的參數和槽函數的參數是對應的!管理器還提供了發送HTTP POST請求的:post()函數HTTP PUT請求的put()函數HTTP DELETE請求的deleteResource()請求 */ }MainWindow::~MainWindow() {delete ui; }void MainWindow::replyFinished(QNetworkReply *reply) {QString all = reply->readAll();ui->textBrowser->setText(all);//僅以文本形式呈現網頁!reply->deleteLater(); }使用HTTP協議從Web服務器上下載一個文件:
下面的例子從Web服務器上下載一個文件,需要輸入正確的URL路徑,
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QNetworkReply; class QNetworkAccessManager; #include <QUrl> class QFile;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();void startRequest(QUrl url);private:Ui::MainWindow *ui;QNetworkAccessManager *manager;QNetworkReply *reply;QUrl url;QFile *file;private slots:void httpFinished();void httpReadyRead();void updateDataReadProgress(qint64, qint64);void on_pushButton_clicked(); };#endif // MAINWINDOW_Hmainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtNetwork>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);manager = new QNetworkAccessManager(this);ui->progressBar->hide(); }MainWindow::~MainWindow() {delete ui; }/*URL要寫得規范,沒有寫http://會報錯;對于Web服務器,當然要指明協議是http:// 我們平時在瀏覽器上只輸入網站是因為瀏覽器幫我們自動補全了http://如果服務器是ftp服務器,當然要輸入完整的ftp://如果服務器是sftp服務器,輸入sftp://101.132.67.215就可以下載Web服務器上的文件了,就像打開本地文件一樣地發開并保存遠端Web服務器上的文件!http://www.yangbo.pro/yangboresumeE2.pdf http://www.yangbo.pro/piece3.jpghttp://www.yangbo.pro/images/10project2.png\然后到C:\Users\Administrator\Desktop\QtNetwork\18-2\build-myHTTP-Desktop_Qt_5_8_0_MSVC2013_32bit-Debug 編譯文件目錄下就可以看到下載好了文件了! */void MainWindow::startRequest(QUrl url) {/*get()函數返回一個QNetworkReply對象,QNetworkReply對象里就有readyRead、downloadProgress、finished這些信號!*/reply = manager->get(QNetworkRequest(url));connect(reply, &QNetworkReply::readyRead, this, &MainWindow::httpReadyRead);connect(reply, &QNetworkReply::downloadProgress,this, &MainWindow::updateDataReadProgress);connect(reply, &QNetworkReply::finished, this, &MainWindow::httpFinished); }void MainWindow::httpReadyRead() {if (file) file->write(reply->readAll()); }void MainWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes) {ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesRead); }void MainWindow::httpFinished() {ui->progressBar->hide();if(file) {file->close();delete file;file = 0;}reply->deleteLater();reply = 0; }void MainWindow::on_pushButton_clicked() {url = ui->lineEdit->text();QFileInfo info(url.path());QString fileName(info.fileName());if (fileName.isEmpty()) fileName = "index.html";file = new QFile(fileName);if(!file->open(QIODevice::WriteOnly)){delete file;file = 0;return;}startRequest(url);ui->progressBar->setValue(0);ui->progressBar->show(); }/**Windows命令行: C:\Users\Administrator>ftp ftp> open www.yangbo.pro 連接到 www.yangbo.pro。 220---------- Welcome to Pure-FTPd [privsep] ---------- 220-You are user number 1 of 5000 allowed. 220-Local time is now 10:06. Server port: 21. 220-This is a private system - No anonymous login 220-IPv6 connections are also welcome on this server. 220 You will be disconnected after 15 minutes of inactivity. 200 OK, UTF-8 enabled 用戶(www.yangbo.pro:(none)): root 331 User root OK. Password required 密碼: 421 Unable to read the indexed puredb file (or old format detected) - Try pure-pw mkdb 遠程主機關閉連接。 ftp> */下面這個小例子用ftp協議從指定的ftp服務器上下載一個文件,服務器端主機名、登陸的用戶名和密碼等都再代碼里指定:
.h文件
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QNetworkReply; class QNetworkAccessManager; #include <QUrl> class QFile;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();void startRequest(QUrl url);private:Ui::MainWindow *ui;QNetworkAccessManager *manager;QNetworkReply *reply;QUrl url;QFile *file;private slots:void httpFinished();void httpReadyRead();void updateDataReadProgress(qint64, qint64);void on_pushButton_clicked(); };#endif // MAINWINDOW_H.cpp文件
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtNetwork>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);manager = new QNetworkAccessManager(this);ui->progressBar->hide(); }MainWindow::~MainWindow() {delete ui; }void MainWindow::startRequest(QUrl url) {reply = manager->get(QNetworkRequest(url));connect(reply, &QNetworkReply::readyRead, this, &MainWindow::httpReadyRead);connect(reply, &QNetworkReply::downloadProgress,this, &MainWindow::updateDataReadProgress);connect(reply, &QNetworkReply::finished, this, &MainWindow::httpFinished); }void MainWindow::httpReadyRead() {if (file) file->write(reply->readAll()); }void MainWindow::updateDataReadProgress(qint64 bytesRead, qint64 totalBytes) {ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesRead); }void MainWindow::httpFinished() {ui->progressBar->hide();if(file) {file->close();delete file;file = 0;}reply->deleteLater();reply = 0; }void MainWindow::on_pushButton_clicked() {url = ui->lineEdit->text();QUrl url;url.setScheme("ftp");url.setHost("v0.ftp.upyun.com");url.setPath("readme.txt");url.setUserName("qtertest/qtftptest");url.setPassword("pwd123456");/*Qt Creator多行注釋和取消多行注釋快捷鍵:Ctrl+/ */// QUrl url;// url.setScheme("sftp");// url.setHost("101.132.67.215");// url.setPath("./data/wwwroot/default/images/10project2.png");// url.setUserName("root");// url.setPassword("159114");QFileInfo info(url.path());QString fileName(info.fileName());if (fileName.isEmpty()) fileName = "index.html";file = new QFile(fileName);if(!file->open(QIODevice::WriteOnly)){delete file;file = 0;return;}startRequest(url);ui->progressBar->setValue(0);ui->progressBar->show(); }4.FTP
FTP(File Transfer Protocol,文件傳輸協議)是一個用于瀏覽遠程ftp服務器主機目錄和下載文件的協議。
FTP協議使用兩個TCP網絡連接,一個用來發送控制命令(端口號:21),一個用來傳輸數據(端口號:20);(而STFP的端口號是22)。
FTP協議有一個狀態,并且需要客戶端在傳輸文件之前發送一些控制命令。
FTP客戶端建立一個連接,并且在整個會話期間一直保持打開。在每個會話期間,可以發生多個傳輸。
下面的這個例子實現瀏覽遠程ftp服務器的文件和目錄,并可下載任意文件:
.pro文件:
#------------------------------------------------- # # Project created by QtCreator 2016-07-10T00:49:54 # #-------------------------------------------------QT += core gui QT += network greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = myFTP TEMPLATE = appSOURCES += main.cpp\mainwindow.cpp \qftp.cpp \qurlinfo.cppHEADERS += mainwindow.h \qftp.h \qurlinfo.hFORMS += mainwindow.uimainwindow.h:
#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> class QFtp; #include <QHash> class QFile; class QUrlInfo; class QTreeWidgetItem;namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);~MainWindow();private:Ui::MainWindow *ui;QFtp *ftp;// 用來存儲一個路徑是否為目錄的信息QHash<QString, bool> isDirectory;// 用來存儲當前路徑QString currentPath;// 用來表示下載的文件QFile *file;private slots:void ftpCommandStarted(int);void ftpCommandFinished(int, bool);// 更新進度條void updateDataTransferProgress(qint64, qint64 );// 將服務器上的文件添加到Tree Widget部件中void addToList(const QUrlInfo &urlInfo);// 雙擊一個目錄時顯示其內容void processItem(QTreeWidgetItem*, int);void on_connectButton_clicked();void on_cdToParentButton_clicked();void on_downloadButton_clicked(); };#endif // MAINWINDOW_Hqftp.h
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.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 the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/#ifndef QFTP_H #define QFTP_H#include <QtCore/qstring.h> #include <QtCore/qobject.h> #include <qurlinfo.h>QT_BEGIN_NAMESPACEclass QFtpPrivate;class QFtp : public QObject {Q_OBJECTpublic:explicit QFtp(QObject *parent = 0);virtual ~QFtp();enum State {Unconnected,HostLookup,Connecting,Connected,LoggedIn,Closing};enum Error {NoError,UnknownError,HostNotFound,ConnectionRefused,NotConnected};enum Command {None,SetTransferMode,SetProxy,ConnectToHost,Login,Close,List,Cd,Get,Put,Remove,Mkdir,Rmdir,Rename,RawCommand};enum TransferMode {Active,Passive};enum TransferType {Binary,Ascii};int setProxy(const QString &host, quint16 port);int connectToHost(const QString &host, quint16 port=21);int login(const QString &user = QString(), const QString &password = QString());int close();int setTransferMode(TransferMode mode);int list(const QString &dir = QString());int cd(const QString &dir);int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);int put(const QByteArray &data, const QString &file, TransferType type = Binary);int put(QIODevice *dev, const QString &file, TransferType type = Binary);int remove(const QString &file);int mkdir(const QString &dir);int rmdir(const QString &dir);int rename(const QString &oldname, const QString &newname);int rawCommand(const QString &command);qint64 bytesAvailable() const;qint64 read(char *data, qint64 maxlen);QByteArray readAll();int currentId() const;QIODevice* currentDevice() const;Command currentCommand() const;bool hasPendingCommands() const;void clearPendingCommands();State state() const;Error error() const;QString errorString() const;public Q_SLOTS:void abort();Q_SIGNALS:void stateChanged(int);void listInfo(const QUrlInfo&);void readyRead();void dataTransferProgress(qint64, qint64);void rawCommandReply(int, const QString&);void commandStarted(int);void commandFinished(int, bool);void done(bool);private:Q_DISABLE_COPY(QFtp)QScopedPointer<QFtpPrivate> d;Q_PRIVATE_SLOT(d, void _q_startNextCommand())Q_PRIVATE_SLOT(d, void _q_piFinished(const QString&))Q_PRIVATE_SLOT(d, void _q_piError(int, const QString&))Q_PRIVATE_SLOT(d, void _q_piConnectState(int))Q_PRIVATE_SLOT(d, void _q_piFtpReply(int, const QString&)) };QT_END_NAMESPACE#endif // QFTP_Hqurlinfo.h
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.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 the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/#ifndef QURLINFO_H #define QURLINFO_H#include <QtCore/qdatetime.h> #include <QtCore/qstring.h> #include <QtCore/qiodevice.h>QT_BEGIN_NAMESPACEclass QUrl; class QUrlInfoPrivate;class QUrlInfo { public:enum PermissionSpec {ReadOwner = 00400, WriteOwner = 00200, ExeOwner = 00100,ReadGroup = 00040, WriteGroup = 00020, ExeGroup = 00010,ReadOther = 00004, WriteOther = 00002, ExeOther = 00001 };QUrlInfo();QUrlInfo(const QUrlInfo &ui);QUrlInfo(const QString &name, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable);QUrlInfo(const QUrl &url, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable);QUrlInfo &operator=(const QUrlInfo &ui);virtual ~QUrlInfo();virtual void setName(const QString &name);virtual void setDir(bool b);virtual void setFile(bool b);virtual void setSymLink(bool b);virtual void setOwner(const QString &s);virtual void setGroup(const QString &s);virtual void setSize(qint64 size);virtual void setWritable(bool b);virtual void setReadable(bool b);virtual void setPermissions(int p);virtual void setLastModified(const QDateTime &dt);void setLastRead(const QDateTime &dt);bool isValid() const;QString name() const;int permissions() const;QString owner() const;QString group() const;qint64 size() const;QDateTime lastModified() const;QDateTime lastRead() const;bool isDir() const;bool isFile() const;bool isSymLink() const;bool isWritable() const;bool isReadable() const;bool isExecutable() const;static bool greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy);static bool lessThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy);static bool equal(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy);bool operator==(const QUrlInfo &i) const;inline bool operator!=(const QUrlInfo &i) const{ return !operator==(i); }private:QUrlInfoPrivate *d; };QT_END_NAMESPACE#endif // QURLINFO_Hmain.cpp
#include "mainwindow.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.show();return a.exec(); }mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "qftp.h" #include <QTextCodec> #include <QFile> #include <QTreeWidgetItem>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);ui->progressBar->setValue(0);connect(ui->fileList, &QTreeWidget::itemActivated,this, &MainWindow::processItem); }MainWindow::~MainWindow() {delete ui; }void MainWindow::ftpCommandStarted(int) {int id = ftp->currentCommand();switch (id){case QFtp::ConnectToHost :ui->label->setText(tr("正在連接到服務器…"));break;case QFtp::Login :ui->label->setText(tr("正在登錄…"));break;case QFtp::Get :ui->label->setText(tr("正在下載…"));break;case QFtp::Close :ui->label->setText(tr("正在關閉連接…"));} }void MainWindow::ftpCommandFinished(int, bool error) {if(ftp->currentCommand() == QFtp::ConnectToHost) {if (error)ui->label->setText(tr("連接服務器出現錯誤:%1").arg(ftp->errorString()));else ui->label->setText(tr("連接到服務器成功"));} else if (ftp->currentCommand() == QFtp::Login) {if (error)ui->label->setText(tr("登錄出現錯誤:%1").arg(ftp->errorString()));else {ui->label->setText(tr("登錄成功"));ftp->list(); //login命令結束時調用了list()函數來顯示目錄中的內容。}} else if (ftp->currentCommand() == QFtp::Get) {if(error)ui->label->setText(tr("下載出現錯誤:%1").arg(ftp->errorString()));else {ui->label->setText(tr("已經完成下載"));file->close();}ui->downloadButton->setEnabled(true);} else if (ftp->currentCommand() == QFtp::List) //對QFtp::List命令的判斷{if (isDirectory.isEmpty())//如果目錄為空,則進行提示{ui->fileList->addTopLevelItem(new QTreeWidgetItem(QStringList()<< tr("<empty>")));ui->fileList->setEnabled(false);ui->label->setText(tr("該目錄為空"));}}else if (ftp->currentCommand() == QFtp::Close){ui->label->setText(tr("已經關閉連接"));} }// 連接按鈕 void MainWindow::on_connectButton_clicked() {ui->fileList->clear();currentPath.clear();isDirectory.clear();ui->progressBar->setValue(0);ftp = new QFtp(this); //創建QFtp對象connect(ftp, &QFtp::commandStarted, this,&MainWindow::ftpCommandStarted);connect(ftp, &QFtp::commandFinished,this, &MainWindow::ftpCommandFinished);connect(ftp, &QFtp::listInfo, this, &MainWindow::addToList);connect(ftp, &QFtp::dataTransferProgress,this, &MainWindow::updateDataTransferProgress);//一下登陸ftp服務器QString ftpServer = ui->ftpServerLineEdit->text();QString userName = ui->userNameLineEdit->text();QString passWord = ui->passWordLineEdit->text();ftp->connectToHost(ftpServer, 21);//ftp發送控制命令的端口號是21端口號,傳輸數據的端口號是20ftp->login(userName, passWord); }void MainWindow::addToList(const QUrlInfo &urlInfo) {// 注意:因為服務器上文件使用UTF-8編碼,所以要進行編碼轉換,這樣顯示中文才不會亂碼QString name = QString::fromUtf8(urlInfo.name().toLatin1());QString owner = QString::fromUtf8(urlInfo.owner().toLatin1());QString group = QString::fromUtf8(urlInfo.group().toLatin1());QTreeWidgetItem *item = new QTreeWidgetItem;item->setText(0, name);item->setText(1, QString::number(urlInfo.size()));item->setText(2, owner);item->setText(3, group);item->setText(4, urlInfo.lastModified().toString("yyyy-MM-dd"));QPixmap pixmap(urlInfo.isDir() ? "../myFTP/dir.png" : "../myFTP/file.png");item->setIcon(0, pixmap);isDirectory[name] = urlInfo.isDir();ui->fileList->addTopLevelItem(item);if (!ui->fileList->currentItem()) {ui->fileList->setCurrentItem(ui->fileList->topLevelItem(0));ui->fileList->setEnabled(true);} }void MainWindow::processItem(QTreeWidgetItem *item, int) {// 如果這個文件是個目錄,則打開if (isDirectory.value(item->text(0))) {// 注意:因為目錄名稱可能是中文,在使用ftp命令cd()前需要先進行編碼轉換QString name = QLatin1String(item->text(0).toUtf8());ui->fileList->clear();isDirectory.clear();currentPath += "/";currentPath += name;ftp->cd(name);ftp->list();ui->cdToParentButton->setEnabled(true);} }// 返回上級目錄按鈕 void MainWindow::on_cdToParentButton_clicked() {ui->fileList->clear();isDirectory.clear();currentPath = currentPath.left(currentPath.lastIndexOf('/'));if (currentPath.isEmpty()) {ui->cdToParentButton->setEnabled(false);ftp->cd("/");} else {ftp->cd(currentPath);}ftp->list(); }// 下載按鈕 void MainWindow::on_downloadButton_clicked() {// 注意:因為文件名稱可能是中文,所以在使用get()函數前需要進行編碼轉換QString fileName = ui->fileList->currentItem()->text(0);QString name = QLatin1String(fileName.toUtf8());file = new QFile(fileName);if (!file->open(QIODevice::WriteOnly)) {delete file;return;}ui->downloadButton->setEnabled(false);ftp->get(name, file); }void MainWindow::updateDataTransferProgress(qint64 readBytes,qint64 totalBytes) {ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(readBytes); }qtfp.cpp
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.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 the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************///#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" #include "qlocale.h"QT_BEGIN_NAMESPACEclass QFtpPI;/*The QFtpDTP (DTP = Data Transfer Process) controls all client sidedata 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();void dataReadyRead();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 way signals: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 58enum 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 QBasicAtomicInt idCounter; };QBasicAtomicInt QFtpCommand::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, const QByteArray &ba): command(cmd), rawCmds(raw), is_ba(true) {id = idCounter.fetchAndAddRelaxed(1);data.ba = new QByteArray(ba); }QFtpCommand::QFtpCommand(QFtp::Command cmd, QStringList raw, QIODevice *dev): command(cmd), rawCmds(raw), is_ba(false) {id = idCounter.fetchAndAddRelaxed(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 = 0;}socket = new QTcpSocket(this); #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the socketsocket->setProperty("_q_networksession", property("_q_networksession")); #endifsocket->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) { #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the socketlistener.setProperty("_q_networksession", property("_q_networksession")); #endifif (!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 = qMin(maxlen, qint64(bytesFromSocket.size()));memcpy(data, bytesFromSocket.data(), read);bytesFromSocket.remove(0, read);}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()); #endifif (data.ba->size() == 0)emit dataTransferProgress(0, bytesTotal);elsesocket->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];qint64 read = data.dev->read(buf, blockSize); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::writeData: write() of size %lli bytes", read); #endifif (read > 0) {socket->write(buf, read);} else if (read == -1 || (!data.dev->isSequential() && data.dev->atEnd())) {// error or EOFif (bytesDone == 0 && socket->bytesToWrite() == 0)emit dataTransferProgress(0, bytesTotal);socket->close();clearData();}// do we continue uploading?callWriteData = data.dev != 0;} }void QFtpDTP::dataReadyRead() {writeData(); }inline bool QFtpDTP::hasError() const {return !err.isNull(); }inline QString QFtpDTP::errorMessage() const {return err; }inline void QFtpDTP::clearError() {err.clear(); }void QFtpDTP::abortConnection() { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::abortConnection, bytesAvailable == %lli",socket ? socket->bytesAvailable() : (qint64) 0); #endifcallWriteData = false;clearData();if (socket)socket->abort(); }static void _q_fixupDateTime(QDateTime *dateTime) {// Adjust for future tolerance.const int futureTolerance = 86400;if (dateTime->secsTo(QDateTime::currentDateTime()) < -futureTolerance) {QDate d = dateTime->date();d.setDate(d.year() - 1, d.month(), d.day());dateTime->setDate(d);} }static void _q_parseUnixDir(const QStringList &tokens, const QString &userName, QUrlInfo *info) {// Unix style, 7 + 1 entries// -rw-r--r-- 1 ftp ftp 17358091 Aug 10 2004 qt-x11-free-3.3.3.tar.gz// drwxr-xr-x 3 ftp ftp 4096 Apr 14 2000 compiled-examples// lrwxrwxrwx 1 ftp ftp 9 Oct 29 2005 qtscape -> qtmozillaif (tokens.size() != 8)return;char first = tokens.at(1).at(0).toLatin1();if (first == 'd') {info->setDir(true);info->setFile(false);info->setSymLink(false);} else if (first == '-') {info->setDir(false);info->setFile(true);info->setSymLink(false);} else if (first == 'l') {info->setDir(true);info->setFile(false);info->setSymLink(true);}// Resolve filenameQString name = tokens.at(7);if (info->isSymLink()) {int linkPos = name.indexOf(QLatin1String(" ->"));if (linkPos != -1)name.resize(linkPos);}info->setName(name);// Resolve owner & groupinfo->setOwner(tokens.at(3));info->setGroup(tokens.at(4));// Resolve sizeinfo->setSize(tokens.at(5).toLongLong());QStringList formats;formats << QLatin1String("MMM dd yyyy") << QLatin1String("MMM dd hh:mm") << QLatin1String("MMM d yyyy")<< QLatin1String("MMM d hh:mm") << QLatin1String("MMM d yyyy") << QLatin1String("MMM dd yyyy");QString dateString = tokens.at(6);dateString[0] = dateString[0].toUpper();// Resolve the modification date by parsing all possible formatsQDateTime dateTime;int n = 0; #ifndef QT_NO_DATESTRINGdo {dateTime = QLocale::c().toDateTime(dateString, formats.at(n++));} while (n < formats.size() && (!dateTime.isValid())); #endifif (n == 2 || n == 4) {// Guess the year.dateTime.setDate(QDate(QDate::currentDate().year(),dateTime.date().month(),dateTime.date().day()));_q_fixupDateTime(&dateTime);}if (dateTime.isValid())info->setLastModified(dateTime);// Resolve permissionsint permissions = 0;QString p = tokens.at(2);permissions |= (p[0] == QLatin1Char('r') ? QUrlInfo::ReadOwner : 0);permissions |= (p[1] == QLatin1Char('w') ? QUrlInfo::WriteOwner : 0);permissions |= (p[2] == QLatin1Char('x') ? QUrlInfo::ExeOwner : 0);permissions |= (p[3] == QLatin1Char('r') ? QUrlInfo::ReadGroup : 0);permissions |= (p[4] == QLatin1Char('w') ? QUrlInfo::WriteGroup : 0);permissions |= (p[5] == QLatin1Char('x') ? QUrlInfo::ExeGroup : 0);permissions |= (p[6] == QLatin1Char('r') ? QUrlInfo::ReadOther : 0);permissions |= (p[7] == QLatin1Char('w') ? QUrlInfo::WriteOther : 0);permissions |= (p[8] == QLatin1Char('x') ? QUrlInfo::ExeOther : 0);info->setPermissions(permissions);bool isOwner = info->owner() == userName;info->setReadable((permissions & QUrlInfo::ReadOther) || ((permissions & QUrlInfo::ReadOwner) && isOwner));info->setWritable((permissions & QUrlInfo::WriteOther) || ((permissions & QUrlInfo::WriteOwner) && isOwner)); }static void _q_parseDosDir(const QStringList &tokens, const QString &userName, QUrlInfo *info) {// DOS style, 3 + 1 entries// 01-16-02 11:14AM <DIR> epsgroup// 06-05-03 03:19PM 1973 readme.txtif (tokens.size() != 4)return;Q_UNUSED(userName);QString name = tokens.at(3);info->setName(name);info->setSymLink(name.toLower().endsWith(QLatin1String(".lnk")));if (tokens.at(2) == QLatin1String("<DIR>")) {info->setFile(false);info->setDir(true);} else {info->setFile(true);info->setDir(false);info->setSize(tokens.at(2).toLongLong());}// Note: We cannot use QFileInfo; permissions are for the server-side// machine, and QFileInfo's behavior depends on the local platform.int permissions = QUrlInfo::ReadOwner | QUrlInfo::WriteOwner| QUrlInfo::ReadGroup | QUrlInfo::WriteGroup| QUrlInfo::ReadOther | QUrlInfo::WriteOther;QString ext;int extIndex = name.lastIndexOf(QLatin1Char('.'));if (extIndex != -1)ext = name.mid(extIndex + 1);if (ext == QLatin1String("exe") || ext == QLatin1String("bat") || ext == QLatin1String("com"))permissions |= QUrlInfo::ExeOwner | QUrlInfo::ExeGroup | QUrlInfo::ExeOther;info->setPermissions(permissions);info->setReadable(true);info->setWritable(info->isFile());QDateTime dateTime; #ifndef QT_NO_DATESTRINGdateTime = QLocale::c().toDateTime(tokens.at(1), QLatin1String("MM-dd-yy hh:mmAP"));if (dateTime.date().year() < 1971) {dateTime.setDate(QDate(dateTime.date().year() + 100,dateTime.date().month(),dateTime.date().day()));} #endifinfo->setLastModified(dateTime);}bool QFtpDTP::parseDir(const QByteArray &buffer, const QString &userName, QUrlInfo *info) {if (buffer.isEmpty())return false;QString bufferStr = QString::fromLatin1(buffer).trimmed();// Unix style FTP serversQRegExp unixPattern(QLatin1String("^([\\-dl])([a-zA-Z\\-]{9,9})\\s+\\d+\\s+(\\S*)\\s+""(\\S*)\\s+(\\d+)\\s+(\\S+\\s+\\S+\\s+\\S+)\\s+(\\S.*)"));if (unixPattern.indexIn(bufferStr) == 0) {_q_parseUnixDir(unixPattern.capturedTexts(), userName, info);return true;}// DOS style FTP serversQRegExp dosPattern(QLatin1String("^(\\d\\d-\\d\\d-\\d\\d\\d?\\d?\\ \\ \\d\\d:\\d\\d[AP]M)\\s+""(<DIR>|\\d+)\\s+(\\S.*)$"));if (dosPattern.indexIn(bufferStr) == 0) {_q_parseDosDir(dosPattern.capturedTexts(), userName, info);return true;}// Unsupportedreturn false; }void QFtpDTP::socketConnected() {bytesDone = 0; #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsConnected)"); #endifemit connectState(QFtpDTP::CsConnected); }void QFtpDTP::socketReadyRead() {if (!socket)return;if (pi->currentCommand().isEmpty()) {socket->close(); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsClosed)"); #endifemit connectState(QFtpDTP::CsClosed);return;}if (pi->abortState == QFtpPI::AbortStarted) {// discard datasocket->readAll();return;}if (pi->currentCommand().startsWith(QLatin1String("LIST"))) {while (socket->canReadLine()) {QUrlInfo i;QByteArray line = socket->readLine(); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP read (list): '%s'", line.constData()); #endifif (parseDir(line, QLatin1String(""), &i)) {emit listInfo(i);} else {// some FTP servers don't return a 550 if the file or directory// does not exist, but rather write a text to the data socket// -- try to catch these casesif (line.endsWith("No such file or directory\r\n"))err = QString::fromLatin1(line);}}} else {if (!is_ba && data.dev) {do {QByteArray ba;ba.resize(socket->bytesAvailable());qint64 bytesRead = socket->read(ba.data(), ba.size());if (bytesRead < 0) {// a read following a readyRead() signal will// never fail.return;}ba.resize(bytesRead);bytesDone += bytesRead; #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP read: %lli bytes (total %lli bytes)", bytesRead, bytesDone); #endifif (data.dev) // make sure it wasn't deleted in the slotdata.dev->write(ba);emit dataTransferProgress(bytesDone, bytesTotal);// Need to loop; dataTransferProgress is often connected to// slots that update the GUI (e.g., progress bar values), and// if events are processed, more data may have arrived.} while (socket->bytesAvailable());} else { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP readyRead: %lli bytes available (total %lli bytes read)",bytesAvailable(), bytesDone); #endifemit dataTransferProgress(bytesDone+socket->bytesAvailable(), bytesTotal);emit readyRead();}} }void QFtpDTP::socketError(QAbstractSocket::SocketError e) {if (e == QTcpSocket::HostNotFoundError) { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsHostNotFound)"); #endifemit connectState(QFtpDTP::CsHostNotFound);} else if (e == QTcpSocket::ConnectionRefusedError) { #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsConnectionRefused)"); #endifemit connectState(QFtpDTP::CsConnectionRefused);} }void QFtpDTP::socketConnectionClosed() {if (!is_ba && data.dev) {clearData();}bytesFromSocket = socket->readAll(); #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::connectState(CsClosed)"); #endifemit connectState(QFtpDTP::CsClosed); }void QFtpDTP::socketBytesWritten(qint64 bytes) {bytesDone += bytes; #if defined(QFTPDTP_DEBUG)qDebug("QFtpDTP::bytesWritten(%lli)", bytesDone); #endifemit dataTransferProgress(bytesDone, bytesTotal);if (callWriteData)writeData(); }void QFtpDTP::setupSocket() {socket = listener.nextPendingConnection();socket->setObjectName(QLatin1String("QFtpDTP Active 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)));listener.close(); }void QFtpDTP::clearData() {is_ba = false;data.dev = 0; }/************************************************************************ QFtpPI implemenatation**********************************************************************/ QFtpPI::QFtpPI(QObject *parent) :QObject(parent),rawCommand(false),transferConnectionExtended(true),dtp(this),commandSocket(0),state(Begin), abortState(None),currentCmd(QString()),waitForDtpToConnect(false),waitForDtpToClose(false) {commandSocket.setObjectName(QLatin1String("QFtpPI_socket"));connect(&commandSocket, SIGNAL(hostFound()),SLOT(hostFound()));connect(&commandSocket, SIGNAL(connected()),SLOT(connected()));connect(&commandSocket, SIGNAL(disconnected()),SLOT(connectionClosed()));connect(&commandSocket, SIGNAL(readyRead()),SLOT(readyRead()));connect(&commandSocket, SIGNAL(error(QAbstractSocket::SocketError)),SLOT(error(QAbstractSocket::SocketError)));connect(&dtp, SIGNAL(connectState(int)),SLOT(dtpConnectState(int))); }void QFtpPI::connectToHost(const QString &host, quint16 port) {emit connectState(QFtp::HostLookup); #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the socket & DTPcommandSocket.setProperty("_q_networksession", property("_q_networksession"));dtp.setProperty("_q_networksession", property("_q_networksession")); #endifcommandSocket.connectToHost(host, port); }/*Sends the sequence of commands \a cmds to the FTP server. When the commandsare all done the finished() signal is emitted. When an error occurs, theerror() signal is emitted.If there are pending commands in the queue this functions returns false andthe \a cmds are not added to the queue; otherwise it returns true. */ bool QFtpPI::sendCommands(const QStringList &cmds) {if (!pendingCommands.isEmpty())return false;if (commandSocket.state() != QTcpSocket::ConnectedState || state!=Idle) {emit error(QFtp::NotConnected, QFtp::tr("Not connected"));return true; // there are no pending commands}pendingCommands = cmds;startNextCmd();return true; }void QFtpPI::clearPendingCommands() {pendingCommands.clear();dtp.abortConnection();currentCmd.clear();state = Idle; }void QFtpPI::abort() {pendingCommands.clear();if (abortState != None)// ABOR already sentreturn;abortState = AbortStarted; #if defined(QFTPPI_DEBUG)qDebug("QFtpPI send: ABOR"); #endifcommandSocket.write("ABOR\r\n", 6);if (currentCmd.startsWith(QLatin1String("STOR ")))dtp.abortConnection(); }void QFtpPI::hostFound() {emit connectState(QFtp::Connecting); }void QFtpPI::connected() {state = Begin; #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [connected()]", state); #endif// try to improve performance by setting TCP_NODELAYcommandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);emit connectState(QFtp::Connected); }void QFtpPI::connectionClosed() {commandSocket.close();emit connectState(QFtp::Unconnected); }void QFtpPI::delayedCloseFinished() {emit connectState(QFtp::Unconnected); }void QFtpPI::error(QAbstractSocket::SocketError e) {if (e == QTcpSocket::HostNotFoundError) {emit connectState(QFtp::Unconnected);emit error(QFtp::HostNotFound,QFtp::tr("Host %1 not found").arg(commandSocket.peerName()));} else if (e == QTcpSocket::ConnectionRefusedError) {emit connectState(QFtp::Unconnected);emit error(QFtp::ConnectionRefused,QFtp::tr("Connection refused to host %1").arg(commandSocket.peerName()));} else if (e == QTcpSocket::SocketTimeoutError) {emit connectState(QFtp::Unconnected);emit error(QFtp::ConnectionRefused,QFtp::tr("Connection timed out to host %1").arg(commandSocket.peerName()));} }void QFtpPI::readyRead() {if (waitForDtpToClose)return;while (commandSocket.canReadLine()) {// read line with respect to line continuationQString line = QString::fromLatin1(commandSocket.readLine());if (replyText.isEmpty()) {if (line.length() < 3) {// protocol errorreturn;}const int lowerLimit[3] = {1,0,0};const int upperLimit[3] = {5,5,9};for (int i=0; i<3; i++) {replyCode[i] = line[i].digitValue();if (replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i]) {// protocol errorreturn;}}}QString endOfMultiLine;endOfMultiLine[0] = '0' + replyCode[0];endOfMultiLine[1] = '0' + replyCode[1];endOfMultiLine[2] = '0' + replyCode[2];endOfMultiLine[3] = QLatin1Char(' ');QString lineCont(endOfMultiLine);lineCont[3] = QLatin1Char('-');QString lineLeft4 = line.left(4);while (lineLeft4 != endOfMultiLine) {if (lineLeft4 == lineCont)replyText += line.mid(4); // strip 'xyz-'elsereplyText += line;if (!commandSocket.canReadLine())return;line = QString::fromLatin1(commandSocket.readLine());lineLeft4 = line.left(4);}replyText += line.mid(4); // strip reply code 'xyz 'if (replyText.endsWith(QLatin1String("\r\n")))replyText.chop(2);if (processReply())replyText = QLatin1String("");} }/*Process a reply from the FTP server.Returns true if the reply was processed or false if the reply has to beprocessed at a later point. */ bool QFtpPI::processReply() { #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [processReply() begin]", state);if (replyText.length() < 400)qDebug("QFtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.toLatin1().constData());elseqDebug("QFtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2]); #endifint replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2];// process 226 replies ("Closing Data Connection") only when the data// connection is really closed to avoid short reads of the DTPif (replyCodeInt == 226 || (replyCodeInt == 250 && currentCmd.startsWith(QLatin1String("RETR")))) {if (dtp.state() != QTcpSocket::UnconnectedState) {waitForDtpToClose = true;return false;}}switch (abortState) {case AbortStarted:abortState = WaitForAbortToFinish;break;case WaitForAbortToFinish:abortState = None;return true;default:break;}// get new statestatic const State table[5] = {/* 1yz 2yz 3yz 4yz 5yz */Waiting, Success, Idle, Failure, Failure};switch (state) {case Begin:if (replyCode[0] == 1) {return true;} else if (replyCode[0] == 2) {state = Idle;emit finished(QFtp::tr("Connected to host %1").arg(commandSocket.peerName()));break;}// reply codes not starting with 1 or 2 are not handled.return true;case Waiting:if (static_cast<signed char>(replyCode[0]) < 0 || replyCode[0] > 5)state = Failure;else #if defined(Q_OS_IRIX) && defined(Q_CC_GNU){// work around a crash on 64 bit gcc IRIXState *t = (State *) table;state = t[replyCode[0] - 1];} #elseif (replyCodeInt == 202)state = Failure;elsestate = table[replyCode[0] - 1]; #endifbreak;default:// ignore unrequested messagereturn true;} #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [processReply() intermediate]", state); #endif// special actions on certain repliesemit rawFtpReply(replyCodeInt, replyText);if (rawCommand) {rawCommand = false;} else if (replyCodeInt == 227) {// 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)// rfc959 does not define this response precisely, and gives// both examples where the parenthesis are used, and where// they are missing. We need to scan for the address and host// info.QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)"));if (addrPortPattern.indexIn(replyText) == -1) { #if defined(QFTPPI_DEBUG)qDebug("QFtp: bad 227 response -- address and port information missing"); #endif// this error should be reported} else {QStringList lst = addrPortPattern.capturedTexts();QString host = lst[1] + QLatin1Char('.') + lst[2] + QLatin1Char('.') + lst[3] + QLatin1Char('.') + lst[4];quint16 port = (lst[5].toUInt() << 8) + lst[6].toUInt();waitForDtpToConnect = true;dtp.connectToHost(host, port);}} else if (replyCodeInt == 229) {// 229 Extended Passive mode OK (|||10982|)int portPos = replyText.indexOf(QLatin1Char('('));if (portPos == -1) { #if defined(QFTPPI_DEBUG)qDebug("QFtp: bad 229 response -- port information missing"); #endif// this error should be reported} else {++portPos;QChar delimiter = replyText.at(portPos);QStringList epsvParameters = replyText.mid(portPos).split(delimiter);waitForDtpToConnect = true;dtp.connectToHost(commandSocket.peerAddress().toString(),epsvParameters.at(3).toInt());}} else if (replyCodeInt == 230) {if (currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 &&pendingCommands.first().startsWith(QLatin1String("PASS "))) {// no need to send the PASS -- we are already logged inpendingCommands.pop_front();}// 230 User logged in, proceed.emit connectState(QFtp::LoggedIn);} else if (replyCodeInt == 213) {// 213 File status.if (currentCmd.startsWith(QLatin1String("SIZE ")))dtp.setBytesTotal(replyText.simplified().toLongLong());} else if (replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR "))) {dtp.waitForConnection();dtp.writeData();}// react on new stateswitch (state) {case Begin:// should never happenbreak;case Success:// success handlingstate = Idle;// no break!case Idle:if (dtp.hasError()) {emit error(QFtp::UnknownError, dtp.errorMessage());dtp.clearError();}startNextCmd();break;case Waiting:// do nothingbreak;case Failure:// If the EPSV or EPRT commands fail, replace them with// the old PASV and PORT instead and try again.if (currentCmd.startsWith(QLatin1String("EPSV"))) {transferConnectionExtended = false;pendingCommands.prepend(QLatin1String("PASV\r\n"));} else if (currentCmd.startsWith(QLatin1String("EPRT"))) {transferConnectionExtended = false;pendingCommands.prepend(QLatin1String("PORT\r\n"));} else {emit error(QFtp::UnknownError, replyText);}if (state != Waiting) {state = Idle;startNextCmd();}break;} #if defined(QFTPPI_DEBUG) // qDebug("QFtpPI state: %d [processReply() end]", state); #endifreturn true; }/*Starts next pending command. Returns false if there are no pending commands,otherwise it returns true. */ bool QFtpPI::startNextCmd() {if (waitForDtpToConnect)// don't process any new commands until we are connectedreturn true;#if defined(QFTPPI_DEBUG)if (state != Idle)qDebug("QFtpPI startNextCmd: Internal error! QFtpPI called in non-Idle state %d", state); #endifif (pendingCommands.isEmpty()) {currentCmd.clear();emit finished(replyText);return false;}currentCmd = pendingCommands.first();// PORT and PASV are edited in-place, depending on whether we// should try the extended transfer connection commands EPRT and// EPSV. The PORT command also triggers setting up a listener, and// the address/port arguments are edited in.QHostAddress address = commandSocket.localAddress();if (currentCmd.startsWith(QLatin1String("PORT"))) {if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended) {int port = dtp.setupListener(address);currentCmd = QLatin1String("EPRT |");currentCmd += (address.protocol() == QTcpSocket::IPv4Protocol) ? QLatin1Char('1') : QLatin1Char('2');currentCmd += QLatin1Char('|') + address.toString() + QLatin1Char('|') + QString::number(port);currentCmd += QLatin1Char('|');} else if (address.protocol() == QTcpSocket::IPv4Protocol) {int port = dtp.setupListener(address);QString portArg;quint32 ip = address.toIPv4Address();portArg += QString::number((ip & 0xff000000) >> 24);portArg += QLatin1Char(',') + QString::number((ip & 0xff0000) >> 16);portArg += QLatin1Char(',') + QString::number((ip & 0xff00) >> 8);portArg += QLatin1Char(',') + QString::number(ip & 0xff);portArg += QLatin1Char(',') + QString::number((port & 0xff00) >> 8);portArg += QLatin1Char(',') + QString::number(port & 0xff);currentCmd = QLatin1String("PORT ");currentCmd += portArg;} else {// No IPv6 connection can be set up with the PORT// command.return false;}currentCmd += QLatin1String("\r\n");} else if (currentCmd.startsWith(QLatin1String("PASV"))) {if ((address.protocol() == QTcpSocket::IPv6Protocol) && transferConnectionExtended)currentCmd = QLatin1String("EPSV\r\n");}pendingCommands.pop_front(); #if defined(QFTPPI_DEBUG)qDebug("QFtpPI send: %s", currentCmd.left(currentCmd.length()-2).toLatin1().constData()); #endifstate = Waiting;commandSocket.write(currentCmd.toLatin1());return true; }void QFtpPI::dtpConnectState(int s) {switch (s) {case QFtpDTP::CsClosed:if (waitForDtpToClose) {// there is an unprocessed replyif (processReply())replyText = QLatin1String("");elsereturn;}waitForDtpToClose = false;readyRead();return;case QFtpDTP::CsConnected:waitForDtpToConnect = false;startNextCmd();return;case QFtpDTP::CsHostNotFound:case QFtpDTP::CsConnectionRefused:emit error(QFtp::ConnectionRefused,QFtp::tr("Connection refused for data connection"));startNextCmd();return;default:return;} }/************************************************************************ QFtpPrivate**********************************************************************/class QFtpPrivate {Q_DECLARE_PUBLIC(QFtp) public:inline QFtpPrivate(QFtp *owner) : close_waitForStateChange(false), state(QFtp::Unconnected),transferMode(QFtp::Passive), error(QFtp::NoError), q_ptr(owner){ }~QFtpPrivate() { while (!pending.isEmpty()) delete pending.takeFirst(); }// private slotsvoid _q_startNextCommand();void _q_piFinished(const QString&);void _q_piError(int, const QString&);void _q_piConnectState(int);void _q_piFtpReply(int, const QString&);int addCommand(QFtpCommand *cmd);QFtpPI pi;QList<QFtpCommand *> pending;bool close_waitForStateChange;QFtp::State state;QFtp::TransferMode transferMode;QFtp::Error error;QString errorString;QString host;quint16 port;QString proxyHost;quint16 proxyPort;QFtp *q_ptr; };int QFtpPrivate::addCommand(QFtpCommand *cmd) {pending.append(cmd);if (pending.count() == 1) {// don't emit the commandStarted() signal before the ID is returnedQTimer::singleShot(0, q_func(), SLOT(_q_startNextCommand()));}return cmd->id; }/************************************************************************ QFtp implementation**********************************************************************/ /*!\class QFtp\brief The QFtp class provides an implementation of the client side of FTP protocol.\ingroup network\inmodule QtNetworkThis class provides a direct interface to FTP that allows you tohave more control over the requests. However, for newapplications, it is recommended to use QNetworkAccessManager andQNetworkReply, as those classes possess a simpler, yet morepowerful API.The class works asynchronously, so there are no blockingfunctions. If an operation cannot be executed immediately, thefunction will still return straight away and the operation will bescheduled for later execution. The results of scheduled operationsare reported via signals. This approach depends on the event loopbeing in operation.The operations that can be scheduled (they are called "commands"in the rest of the documentation) are the following:connectToHost(), login(), close(), list(), cd(), get(), put(),remove(), mkdir(), rmdir(), rename() and rawCommand().All of these commands return a unique identifier that allows youto keep track of the command that is currently being executed.When the execution of a command starts, the commandStarted()signal with the command's identifier is emitted. When the commandis finished, the commandFinished() signal is emitted with thecommand's identifier and a bool that indicates whether the commandfinished with an error.In some cases, you might want to execute a sequence of commands,e.g. if you want to connect and login to a FTP server. This issimply achieved:\snippet doc/src/snippets/code/src_network_access_qftp.cpp 0In this case two FTP commands have been scheduled. When the lastscheduled command has finished, a done() signal is emitted witha bool argument that tells you whether the sequence finished withan error.If an error occurs during the execution of one of the commands ina sequence of commands, all the pending commands (i.e. scheduled,but not yet executed commands) are cleared and no signals areemitted for them.Some commands, e.g. list(), emit additional signals to reporttheir results.Example: If you want to download the INSTALL file from the QtFTP server, you would write this:\snippet doc/src/snippets/code/src_network_access_qftp.cpp 1For this example the following sequence of signals is emitted(with small variations, depending on network traffic, etc.):\snippet doc/src/snippets/code/src_network_access_qftp.cpp 2The dataTransferProgress() signal in the above example is usefulif you want to show a \link QProgressBar progress bar \endlink toinform the user about the progress of the download. ThereadyRead() signal tells you that there is data ready to be read.The amount of data can be queried then with the bytesAvailable()function and it can be read with the read() or readAll()function.If the login fails for the above example, the signals would looklike this:\snippet doc/src/snippets/code/src_network_access_qftp.cpp 3You can then get details about the error with the error() anderrorString() functions.For file transfer, QFtp can use both active or passive mode, andit uses passive file transfer mode by default; see thedocumentation for setTransferMode() for more details about this.Call setProxy() to make QFtp connect via an FTP proxy server.The functions currentId() and currentCommand() provide moreinformation about the currently executing command.The functions hasPendingCommands() and clearPendingCommands()allow you to query and clear the list of pending commands.If you are an experienced network programmer and want to havecomplete control you can use rawCommand() to execute arbitrary FTPcommands.\warning The current version of QFtp doesn't fully supportnon-Unix FTP servers.\sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,{FTP Example} *//*!Constructs a QFtp object with the given \a parent. */ QFtp::QFtp(QObject *parent): QObject(parent), d(new QFtpPrivate(this)) {d->errorString = tr("Unknown error");connect(&d->pi, SIGNAL(connectState(int)),SLOT(_q_piConnectState(int)));connect(&d->pi, SIGNAL(finished(QString)),SLOT(_q_piFinished(QString)));connect(&d->pi, SIGNAL(error(int,QString)),SLOT(_q_piError(int,QString)));connect(&d->pi, SIGNAL(rawFtpReply(int,QString)),SLOT(_q_piFtpReply(int,QString)));connect(&d->pi.dtp, SIGNAL(readyRead()),SIGNAL(readyRead()));connect(&d->pi.dtp, SIGNAL(dataTransferProgress(qint64,qint64)),SIGNAL(dataTransferProgress(qint64,qint64)));connect(&d->pi.dtp, SIGNAL(listInfo(QUrlInfo)),SIGNAL(listInfo(QUrlInfo))); }/*!\enum QFtp::StateThis enum defines the connection state:\value Unconnected There is no connection to the host.\value HostLookup A host name lookup is in progress.\value Connecting An attempt to connect to the host is in progress.\value Connected Connection to the host has been achieved.\value LoggedIn Connection and user login have been achieved.\value Closing The connection is closing down, but it is not yetclosed. (The state will be \c Unconnected when the connection isclosed.)\sa stateChanged() state() */ /*!\enum QFtp::TransferModeFTP works with two socket connections; one for commands andanother for transmitting data. While the command connection isalways initiated by the client, the second connection can beinitiated by either the client or the server.This enum defines whether the client (Passive mode) or the server(Active mode) should set up the data connection.\value Passive The client connects to the server to transmit itsdata.\value Active The server connects to the client to transmit itsdata. */ /*!\enum QFtp::TransferTypeThis enum identifies the data transfer type used with get andput commands.\value Binary The data will be transferred in Binary mode.\value Ascii The data will be transferred in Ascii mode and new linecharacters will be converted to the local format. */ /*!\enum QFtp::ErrorThis enum identifies the error that occurred.\value NoError No error occurred.\value HostNotFound The host name lookup failed.\value ConnectionRefused The server refused the connection.\value NotConnected Tried to send a command, but there is no connection toa server.\value UnknownError An error other than those specified aboveoccurred.\sa error() *//*!\enum QFtp::CommandThis enum is used as the return value for the currentCommand() function.This allows you to perform specific actions for particularcommands, e.g. in a FTP client, you might want to clear thedirectory view when a list() command is started; in this case youcan simply check in the slot connected to the start() signal ifthe currentCommand() is \c List.\value None No command is being executed.\value SetTransferMode set the \link TransferMode transfer\endlink mode.\value SetProxy switch proxying on or off.\value ConnectToHost connectToHost() is being executed.\value Login login() is being executed.\value Close close() is being executed.\value List list() is being executed.\value Cd cd() is being executed.\value Get get() is being executed.\value Put put() is being executed.\value Remove remove() is being executed.\value Mkdir mkdir() is being executed.\value Rmdir rmdir() is being executed.\value Rename rename() is being executed.\value RawCommand rawCommand() is being executed.\sa currentCommand() *//*!\fn void QFtp::stateChanged(int state)This signal is emitted when the state of the connection changes.The argument \a state is the new state of the connection; it isone of the \l State values.It is usually emitted in response to a connectToHost() or close()command, but it can also be emitted "spontaneously", e.g. when theserver closes the connection unexpectedly.\sa connectToHost() close() state() State *//*!\fn void QFtp::listInfo(const QUrlInfo &i);This signal is emitted for each directory entry the list() commandfinds. The details of the entry are stored in \a i.\sa list() *//*!\fn void QFtp::commandStarted(int id)This signal is emitted when processing the command identified by\a id starts.\sa commandFinished() done() *//*!\fn void QFtp::commandFinished(int id, bool error)This signal is emitted when processing the command identified by\a id has finished. \a error is true if an error occurred duringthe processing; otherwise \a error is false.\sa commandStarted() done() error() errorString() *//*!\fn void QFtp::done(bool error)This signal is emitted when the last pending command has finished;(it is emitted after the last command's commandFinished() signal).\a error is true if an error occurred during the processing;otherwise \a error is false.\sa commandFinished() error() errorString() *//*!\fn void QFtp::readyRead()This signal is emitted in response to a get() command when thereis new data to read.If you specify a device as the second argument in the get()command, this signal is \e not emitted; instead the data iswritten directly to the device.You can read the data with the readAll() or read() functions.This signal is useful if you want to process the data in chunks assoon as it becomes available. If you are only interested in thecomplete data, just connect to the commandFinished() signal andread the data then instead.\sa get() read() readAll() bytesAvailable() *//*!\fn void QFtp::dataTransferProgress(qint64 done, qint64 total)This signal is emitted in response to a get() or put() request toindicate the current progress of the download or upload.\a done is the amount of data that has already been transferredand \a total is the total amount of data to be read or written. Itis possible that the QFtp class is not able to determine the totalamount of data that should be transferred, in which case \a totalis 0. (If you connect this signal to a QProgressBar, the progressbar shows a busy indicator if the total is 0).\warning \a done and \a total are not necessarily the size inbytes, since for large files these values might need to be"scaled" to avoid overflow.\sa get(), put(), QProgressBar *//*!\fn void QFtp::rawCommandReply(int replyCode, const QString &detail);This signal is emitted in response to the rawCommand() function.\a replyCode is the 3 digit reply code and \a detail is the textthat follows the reply code.\sa rawCommand() *//*!Connects to the FTP server \a host using port \a port.The stateChanged() signal is emitted when the state of theconnecting process changes, e.g. to \c HostLookup, then \cConnecting, then \c Connected.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa stateChanged() commandStarted() commandFinished() */ int QFtp::connectToHost(const QString &host, quint16 port) {QStringList cmds;cmds << host;cmds << QString::number((uint)port);int id = d->addCommand(new QFtpCommand(ConnectToHost, cmds));d->pi.transferConnectionExtended = true;return id; }/*!Logs in to the FTP server with the username \a user and thepassword \a password.The stateChanged() signal is emitted when the state of theconnecting process changes, e.g. to \c LoggedIn.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::login(const QString &user, const QString &password) {QStringList cmds;cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n"));cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n"));return d->addCommand(new QFtpCommand(Login, cmds)); }/*!Closes the connection to the FTP server.The stateChanged() signal is emitted when the state of theconnecting process changes, e.g. to \c Closing, then \cUnconnected.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa stateChanged() commandStarted() commandFinished() */ int QFtp::close() {return d->addCommand(new QFtpCommand(Close, QStringList(QLatin1String("QUIT\r\n")))); }/*!Sets the current FTP transfer mode to \a mode. The default is QFtp::Passive.\sa QFtp::TransferMode */ int QFtp::setTransferMode(TransferMode mode) {int id = d->addCommand(new QFtpCommand(SetTransferMode, QStringList()));d->pi.transferConnectionExtended = true;d->transferMode = mode;return id; }/*!Enables use of the FTP proxy on host \a host and port \aport. Calling this function with \a host empty disables proxying.QFtp does not support FTP-over-HTTP proxy servers. UseQNetworkAccessManager for this. */ int QFtp::setProxy(const QString &host, quint16 port) {QStringList args;args << host << QString::number(port);return d->addCommand(new QFtpCommand(SetProxy, args)); }/*!Lists the contents of directory \a dir on the FTP server. If \adir is empty, it lists the contents of the current directory.The listInfo() signal is emitted for each directory entry found.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa listInfo() commandStarted() commandFinished() */ int QFtp::list(const QString &dir) {QStringList cmds;cmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");if (dir.isEmpty())cmds << QLatin1String("LIST\r\n");elsecmds << (QLatin1String("LIST ") + dir + QLatin1String("\r\n"));return d->addCommand(new QFtpCommand(List, cmds)); }/*!Changes the working directory of the server to \a dir.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::cd(const QString &dir) {return d->addCommand(new QFtpCommand(Cd, QStringList(QLatin1String("CWD ") + dir + QLatin1String("\r\n")))); }/*!Downloads the file \a file from the server.If \a dev is 0, then the readyRead() signal is emitted when thereis data available to read. You can then read the data with theread() or readAll() functions.If \a dev is not 0, the data is written directly to the device \adev. Make sure that the \a dev pointer is valid for the durationof the operation (it is safe to delete it when thecommandFinished() signal is emitted). In this case the readyRead()signal is \e not emitted and you cannot read data with theread() or readAll() functions.If you don't read the data immediately it becomes available, i.e.when the readyRead() signal is emitted, it is still availableuntil the next command is started.For example, if you want to present the data to the user as soonas there is something available, connect to the readyRead() signaland read the data immediately. On the other hand, if you only wantto work with the complete data, you can connect to thecommandFinished() signal and read the data when the get() commandis finished.The data is transferred as Binary or Ascii depending on the valueof \a type.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa readyRead() dataTransferProgress() commandStarted()commandFinished() */ int QFtp::get(const QString &file, QIODevice *dev, TransferType type) {QStringList cmds;if (type == Binary)cmds << QLatin1String("TYPE I\r\n");elsecmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String("SIZE ") + file + QLatin1String("\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");cmds << QLatin1String("RETR ") + file + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Get, cmds, dev)); }/*!\overloadWrites a copy of the given \a data to the file called \a file onthe server. The progress of the upload is reported by thedataTransferProgress() signal.The data is transferred as Binary or Ascii depending on the valueof \a type.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.Since this function takes a copy of the \a data, you can discardyour own copy when this function returns.\sa dataTransferProgress() commandStarted() commandFinished() */ int QFtp::put(const QByteArray &data, const QString &file, TransferType type) {QStringList cmds;if (type == Binary)cmds << QLatin1String("TYPE I\r\n");elsecmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");cmds << QLatin1String("ALLO ") + QString::number(data.size()) + QLatin1String("\r\n");cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Put, cmds, data)); }/*!Reads the data from the IO device \a dev, and writes it to thefile called \a file on the server. The data is read in chunks fromthe IO device, so this overload allows you to transmit largeamounts of data without the need to read all the data into memoryat once.The data is transferred as Binary or Ascii depending on the valueof \a type.Make sure that the \a dev pointer is valid for the duration of theoperation (it is safe to delete it when the commandFinished() isemitted). */ int QFtp::put(QIODevice *dev, const QString &file, TransferType type) {QStringList cmds;if (type == Binary)cmds << QLatin1String("TYPE I\r\n");elsecmds << QLatin1String("TYPE A\r\n");cmds << QLatin1String(d->transferMode == Passive ? "PASV\r\n" : "PORT\r\n");if (!dev->isSequential())cmds << QLatin1String("ALLO ") + QString::number(dev->size()) + QLatin1String("\r\n");cmds << QLatin1String("STOR ") + file + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Put, cmds, dev)); }/*!Deletes the file called \a file from the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::remove(const QString &file) {return d->addCommand(new QFtpCommand(Remove, QStringList(QLatin1String("DELE ") + file + QLatin1String("\r\n")))); }/*!Creates a directory called \a dir on the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::mkdir(const QString &dir) {return d->addCommand(new QFtpCommand(Mkdir, QStringList(QLatin1String("MKD ") + dir + QLatin1String("\r\n")))); }/*!Removes the directory called \a dir from the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::rmdir(const QString &dir) {return d->addCommand(new QFtpCommand(Rmdir, QStringList(QLatin1String("RMD ") + dir + QLatin1String("\r\n")))); }/*!Renames the file called \a oldname to \a newname on the server.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa commandStarted() commandFinished() */ int QFtp::rename(const QString &oldname, const QString &newname) {QStringList cmds;cmds << QLatin1String("RNFR ") + oldname + QLatin1String("\r\n");cmds << QLatin1String("RNTO ") + newname + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(Rename, cmds)); }/*!Sends the raw FTP command \a command to the FTP server. This isuseful for low-level FTP access. If the operation you wish toperform has an equivalent QFtp function, we recommend using thefunction instead of raw FTP commands since the functions areeasier and safer.The function does not block and returns immediately. The commandis scheduled, and its execution is performed asynchronously. Thefunction returns a unique identifier which is passed bycommandStarted() and commandFinished().When the command is started the commandStarted() signal isemitted. When it is finished the commandFinished() signal isemitted.\sa rawCommandReply() commandStarted() commandFinished() */ int QFtp::rawCommand(const QString &command) {QString cmd = command.trimmed() + QLatin1String("\r\n");return d->addCommand(new QFtpCommand(RawCommand, QStringList(cmd))); }/*!Returns the number of bytes that can be read from the data socketat the moment.\sa get() readyRead() read() readAll() */ qint64 QFtp::bytesAvailable() const {return d->pi.dtp.bytesAvailable(); }/*! \fn qint64 QFtp::readBlock(char *data, quint64 maxlen)Use read() instead. *//*!Reads \a maxlen bytes from the data socket into \a data andreturns the number of bytes read. Returns -1 if an error occurred.\sa get() readyRead() bytesAvailable() readAll() */ qint64 QFtp::read(char *data, qint64 maxlen) {return d->pi.dtp.read(data, maxlen); }/*!Reads all the bytes available from the data socket and returnsthem.\sa get() readyRead() bytesAvailable() read() */ QByteArray QFtp::readAll() {return d->pi.dtp.readAll(); }/*!Aborts the current command and deletes all scheduled commands.If there is an unfinished command (i.e. a command for which thecommandStarted() signal has been emitted, but for which thecommandFinished() signal has not been emitted), this functionsends an \c ABORT command to the server. When the server repliesthat the command is aborted, the commandFinished() signal with the\c error argument set to \c true is emitted for the command. Dueto timing issues, it is possible that the command had alreadyfinished before the abort request reached the server, in whichcase, the commandFinished() signal is emitted with the \c errorargument set to \c false.For all other commands that are affected by the abort(), nosignals are emitted.If you don't start further FTP commands directly after theabort(), there won't be any scheduled commands and the done()signal is emitted.\warning Some FTP servers, for example the BSD FTP daemon (version0.3), wrongly return a positive reply even when an abort hasoccurred. For these servers the commandFinished() signal has itserror flag set to \c false, even though the command did notcomplete successfully.\sa clearPendingCommands() */ void QFtp::abort() {if (d->pending.isEmpty())return;clearPendingCommands();d->pi.abort(); }/*!Returns the identifier of the FTP command that is being executedor 0 if there is no command being executed.\sa currentCommand() */ int QFtp::currentId() const {if (d->pending.isEmpty())return 0;return d->pending.first()->id; }/*!Returns the command type of the FTP command being executed or \cNone if there is no command being executed.\sa currentId() */ QFtp::Command QFtp::currentCommand() const {if (d->pending.isEmpty())return None;return d->pending.first()->command; }/*!Returns the QIODevice pointer that is used by the FTP command to read datafrom or store data to. If there is no current FTP command being executed orif the command does not use an IO device, this function returns 0.This function can be used to delete the QIODevice in the slot connected tothe commandFinished() signal.\sa get() put() */ QIODevice* QFtp::currentDevice() const {if (d->pending.isEmpty())return 0;QFtpCommand *c = d->pending.first();if (c->is_ba)return 0;return c->data.dev; }/*!Returns true if there are any commands scheduled that have not yetbeen executed; otherwise returns false.The command that is being executed is \e not considered as ascheduled command.\sa clearPendingCommands() currentId() currentCommand() */ bool QFtp::hasPendingCommands() const {return d->pending.count() > 1; }/*!Deletes all pending commands from the list of scheduled commands.This does not affect the command that is being executed. If youwant to stop this as well, use abort().\sa hasPendingCommands() abort() */ void QFtp::clearPendingCommands() {// delete all entires except the first onewhile (d->pending.count() > 1)delete d->pending.takeLast(); }/*!Returns the current state of the object. When the state changes,the stateChanged() signal is emitted.\sa State stateChanged() */ QFtp::State QFtp::state() const {return d->state; }/*!Returns the last error that occurred. This is useful to find outwhat went wrong when receiving a commandFinished() or a done()signal with the \c error argument set to \c true.If you start a new command, the error status is reset to \c NoError. */ QFtp::Error QFtp::error() const {return d->error; }/*!Returns a human-readable description of the last error thatoccurred. This is useful for presenting a error message to theuser when receiving a commandFinished() or a done() signal withthe \c error argument set to \c true.The error string is often (but not always) the reply from theserver, so it is not always possible to translate the string. Ifthe message comes from Qt, the string has already passed throughtr(). */ QString QFtp::errorString() const {return d->errorString; }/*! \internal */ void QFtpPrivate::_q_startNextCommand() {Q_Q(QFtp);if (pending.isEmpty())return;QFtpCommand *c = pending.first();error = QFtp::NoError;errorString = QT_TRANSLATE_NOOP(QFtp, QLatin1String("Unknown error"));if (q->bytesAvailable())q->readAll(); // clear the dataemit q->commandStarted(c->id);// Proxy support, replace the Login argument in place, then fall// through.if (c->command == QFtp::Login && !proxyHost.isEmpty()) {QString loginString = c->rawCmds.first().trimmed();loginString += QLatin1Char('@') + host;if (port && port != 21)loginString += QLatin1Char(':') + QString::number(port);loginString += QLatin1String("\r\n");c->rawCmds[0] = loginString;}if (c->command == QFtp::SetTransferMode) {_q_piFinished(QLatin1String("Transfer mode set"));} else if (c->command == QFtp::SetProxy) {proxyHost = c->rawCmds[0];proxyPort = c->rawCmds[1].toUInt();c->rawCmds.clear();_q_piFinished(QLatin1String("Proxy set to ") + proxyHost + QLatin1Char(':') + QString::number(proxyPort));} else if (c->command == QFtp::ConnectToHost) { #ifndef QT_NO_BEARERMANAGEMENT//copy network session down to the PIpi.setProperty("_q_networksession", q->property("_q_networksession")); #endifif (!proxyHost.isEmpty()) {host = c->rawCmds[0];port = c->rawCmds[1].toUInt();pi.connectToHost(proxyHost, proxyPort);} else {pi.connectToHost(c->rawCmds[0], c->rawCmds[1].toUInt());}} else {if (c->command == QFtp::Put) {if (c->is_ba) {pi.dtp.setData(c->data.ba);pi.dtp.setBytesTotal(c->data.ba->size());} else if (c->data.dev && (c->data.dev->isOpen() || c->data.dev->open(QIODevice::ReadOnly))) {pi.dtp.setDevice(c->data.dev);if (c->data.dev->isSequential()) {pi.dtp.setBytesTotal(0);pi.dtp.connect(c->data.dev, SIGNAL(readyRead()), SLOT(dataReadyRead()));pi.dtp.connect(c->data.dev, SIGNAL(readChannelFinished()), SLOT(dataReadyRead()));} else {pi.dtp.setBytesTotal(c->data.dev->size());}}} else if (c->command == QFtp::Get) {if (!c->is_ba && c->data.dev) {pi.dtp.setDevice(c->data.dev);}} else if (c->command == QFtp::Close) {state = QFtp::Closing;emit q->stateChanged(state);}pi.sendCommands(c->rawCmds);} }/*! \internal */ void QFtpPrivate::_q_piFinished(const QString&) {if (pending.isEmpty())return;QFtpCommand *c = pending.first();if (c->command == QFtp::Close) {// The order of in which the slots are called is arbitrary, so// disconnect the SIGNAL-SIGNAL temporary to make sure that we// don't get the commandFinished() signal before the stateChanged()// signal.if (state != QFtp::Unconnected) {close_waitForStateChange = true;return;}}emit q_func()->commandFinished(c->id, false);pending.removeFirst();delete c;if (pending.isEmpty()) {emit q_func()->done(false);} else {_q_startNextCommand();} }/*! \internal */ void QFtpPrivate::_q_piError(int errorCode, const QString &text) {Q_Q(QFtp);if (pending.isEmpty()) {qWarning("QFtpPrivate::_q_piError was called without pending command!");return;}QFtpCommand *c = pending.first();// non-fatal errorsif (c->command == QFtp::Get && pi.currentCommand().startsWith(QLatin1String("SIZE "))) {pi.dtp.setBytesTotal(0);return;} else if (c->command==QFtp::Put && pi.currentCommand().startsWith(QLatin1String("ALLO "))) {return;}error = QFtp::Error(errorCode);switch (q->currentCommand()) {case QFtp::ConnectToHost:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Connecting to host failed:\n%1")).arg(text);break;case QFtp::Login:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Login failed:\n%1")).arg(text);break;case QFtp::List:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Listing directory failed:\n%1")).arg(text);break;case QFtp::Cd:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Changing directory failed:\n%1")).arg(text);break;case QFtp::Get:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Downloading file failed:\n%1")).arg(text);break;case QFtp::Put:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Uploading file failed:\n%1")).arg(text);break;case QFtp::Remove:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing file failed:\n%1")).arg(text);break;case QFtp::Mkdir:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Creating directory failed:\n%1")).arg(text);break;case QFtp::Rmdir:errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QFtp", "Removing directory failed:\n%1")).arg(text);break;default:errorString = text;break;}pi.clearPendingCommands();q->clearPendingCommands();emit q->commandFinished(c->id, true);pending.removeFirst();delete c;if (pending.isEmpty())emit q->done(true);else_q_startNextCommand(); }/*! \internal */ void QFtpPrivate::_q_piConnectState(int connectState) {state = QFtp::State(connectState);emit q_func()->stateChanged(state);if (close_waitForStateChange) {close_waitForStateChange = false;_q_piFinished(QLatin1String(QT_TRANSLATE_NOOP("QFtp", "Connection closed")));} }/*! \internal */ void QFtpPrivate::_q_piFtpReply(int code, const QString &text) {if (q_func()->currentCommand() == QFtp::RawCommand) {pi.rawCommand = true;emit q_func()->rawCommandReply(code, text);} }/*!Destructor. */ QFtp::~QFtp() {abort();close(); }QT_END_NAMESPACE#include "qftp.moc"#include "moc_qftp.cpp"#endif // QT_NO_FTPqurlinfo.cpp
/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.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 the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/#include "qurlinfo.h"#include "qurl.h" #include "qdir.h" #include <limits.h>QT_BEGIN_NAMESPACEclass QUrlInfoPrivate { public:QUrlInfoPrivate() :permissions(0),size(0),isDir(false),isFile(true),isSymLink(false),isWritable(true),isReadable(true),isExecutable(false){}QString name;int permissions;QString owner;QString group;qint64 size;QDateTime lastModified;QDateTime lastRead;bool isDir;bool isFile;bool isSymLink;bool isWritable;bool isReadable;bool isExecutable; };/*!\class QUrlInfo\brief The QUrlInfo class stores information about URLs.\ingroup io\ingroup network\inmodule QtNetworkThe information about a URL that can be retrieved includes name(),permissions(), owner(), group(), size(), lastModified(),lastRead(), isDir(), isFile(), isSymLink(), isWritable(),isReadable() and isExecutable().You can create your own QUrlInfo objects passing in all therelevant information in the constructor, and you can modify aQUrlInfo; for each getter mentioned above there is an equivalentsetter. Note that setting values does not affect the underlyingresource that the QUrlInfo provides information about; for exampleif you call setWritable(true) on a read-only resource the onlything changed is the QUrlInfo object, not the resource.\sa QUrl, {FTP Example} *//*!\enum QUrlInfo::PermissionSpecThis enum is used by the permissions() function to report thepermissions of a file.\value ReadOwner The file is readable by the owner of the file.\value WriteOwner The file is writable by the owner of the file.\value ExeOwner The file is executable by the owner of the file.\value ReadGroup The file is readable by the group.\value WriteGroup The file is writable by the group.\value ExeGroup The file is executable by the group.\value ReadOther The file is readable by anyone.\value WriteOther The file is writable by anyone.\value ExeOther The file is executable by anyone. *//*!Constructs an invalid QUrlInfo object with default values.\sa isValid() */QUrlInfo::QUrlInfo() {d = 0; }/*!Copy constructor, copies \a ui to this URL info object. */QUrlInfo::QUrlInfo(const QUrlInfo &ui) {if (ui.d) {d = new QUrlInfoPrivate;*d = *ui.d;} else {d = 0;} }/*!Constructs a QUrlInfo object by specifying all the URL'sinformation.The information that is passed is the \a name, file \apermissions, \a owner and \a group and the file's \a size. Alsopassed is the \a lastModified date/time and the \a lastReaddate/time. Flags are also passed, specifically, \a isDir, \aisFile, \a isSymLink, \a isWritable, \a isReadable and \aisExecutable. */QUrlInfo::QUrlInfo(const QString &name, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable) {d = new QUrlInfoPrivate;d->name = name;d->permissions = permissions;d->owner = owner;d->group = group;d->size = size;d->lastModified = lastModified;d->lastRead = lastRead;d->isDir = isDir;d->isFile = isFile;d->isSymLink = isSymLink;d->isWritable = isWritable;d->isReadable = isReadable;d->isExecutable = isExecutable; }/*!Constructs a QUrlInfo object by specifying all the URL'sinformation.The information that is passed is the \a url, file \apermissions, \a owner and \a group and the file's \a size. Alsopassed is the \a lastModified date/time and the \a lastReaddate/time. Flags are also passed, specifically, \a isDir, \aisFile, \a isSymLink, \a isWritable, \a isReadable and \aisExecutable. */QUrlInfo::QUrlInfo(const QUrl &url, int permissions, const QString &owner,const QString &group, qint64 size, const QDateTime &lastModified,const QDateTime &lastRead, bool isDir, bool isFile, bool isSymLink,bool isWritable, bool isReadable, bool isExecutable) {d = new QUrlInfoPrivate;d->name = QFileInfo(url.path()).fileName();d->permissions = permissions;d->owner = owner;d->group = group;d->size = size;d->lastModified = lastModified;d->lastRead = lastRead;d->isDir = isDir;d->isFile = isFile;d->isSymLink = isSymLink;d->isWritable = isWritable;d->isReadable = isReadable;d->isExecutable = isExecutable; }/*!Sets the name of the URL to \a name. The name is the full text,for example, "http://qt.nokia.com/doc/qurlinfo.html".If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setName(const QString &name) {if (!d)d = new QUrlInfoPrivate;d->name = name; }/*!If \a b is true then the URL is set to be a directory; if \a b isfalse then the URL is set not to be a directory (which normallymeans it is a file). (Note that a URL can refer to both a file anda directory even though most file systems do not support this.)If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setDir(bool b) {if (!d)d = new QUrlInfoPrivate;d->isDir = b; }/*!If \a b is true then the URL is set to be a file; if \b is falsethen the URL is set not to be a file (which normally means it is adirectory). (Note that a URL can refer to both a file and adirectory even though most file systems do not support this.)If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setFile(bool b) {if (!d)d = new QUrlInfoPrivate;d->isFile = b; }/*!Specifies that the URL refers to a symbolic link if \a b is trueand that it does not if \a b is false.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setSymLink(bool b) {if (!d)d = new QUrlInfoPrivate;d->isSymLink = b; }/*!Specifies that the URL is writable if \a b is true and notwritable if \a b is false.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setWritable(bool b) {if (!d)d = new QUrlInfoPrivate;d->isWritable = b; }/*!Specifies that the URL is readable if \a b is true and notreadable if \a b is false.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setReadable(bool b) {if (!d)d = new QUrlInfoPrivate;d->isReadable = b; }/*!Specifies that the owner of the URL is called \a s.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setOwner(const QString &s) {if (!d)d = new QUrlInfoPrivate;d->owner = s; }/*!Specifies that the owning group of the URL is called \a s.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setGroup(const QString &s) {if (!d)d = new QUrlInfoPrivate;d->group = s; }/*!Specifies the \a size of the URL.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setSize(qint64 size) {if (!d)d = new QUrlInfoPrivate;d->size = size; }/*!Specifies that the URL has access permissions \a p.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setPermissions(int p) {if (!d)d = new QUrlInfoPrivate;d->permissions = p; }/*!Specifies that the object the URL refers to was last modified at\a dt.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setLastModified(const QDateTime &dt) {if (!d)d = new QUrlInfoPrivate;d->lastModified = dt; }/*!\since 4.4Specifies that the object the URL refers to was last read at\a dt.If you call this function for an invalid URL info, this functionturns it into a valid one.\sa isValid() */void QUrlInfo::setLastRead(const QDateTime &dt) {if (!d)d = new QUrlInfoPrivate;d->lastRead = dt; }/*!Destroys the URL info object. */QUrlInfo::~QUrlInfo() {delete d; }/*!Assigns the values of \a ui to this QUrlInfo object. */QUrlInfo &QUrlInfo::operator=(const QUrlInfo &ui) {if (ui.d) {if (!d)d= new QUrlInfoPrivate;*d = *ui.d;} else {delete d;d = 0;}return *this; }/*!Returns the file name of the URL.\sa isValid() */QString QUrlInfo::name() const {if (!d)return QString();return d->name; }/*!Returns the permissions of the URL. You can use the \c PermissionSpec flagsto test for certain permissions.\sa isValid() */int QUrlInfo::permissions() const {if (!d)return 0;return d->permissions; }/*!Returns the owner of the URL.\sa isValid() */QString QUrlInfo::owner() const {if (!d)return QString();return d->owner; }/*!Returns the group of the URL.\sa isValid() */QString QUrlInfo::group() const {if (!d)return QString();return d->group; }/*!Returns the size of the URL.\sa isValid() */qint64 QUrlInfo::size() const {if (!d)return 0;return d->size; }/*!Returns the last modification date of the URL.\sa isValid() */QDateTime QUrlInfo::lastModified() const {if (!d)return QDateTime();return d->lastModified; }/*!Returns the date when the URL was last read.\sa isValid() */QDateTime QUrlInfo::lastRead() const {if (!d)return QDateTime();return d->lastRead; }/*!Returns true if the URL is a directory; otherwise returns false.\sa isValid() */bool QUrlInfo::isDir() const {if (!d)return false;return d->isDir; }/*!Returns true if the URL is a file; otherwise returns false.\sa isValid() */bool QUrlInfo::isFile() const {if (!d)return false;return d->isFile; }/*!Returns true if the URL is a symbolic link; otherwise returns false.\sa isValid() */bool QUrlInfo::isSymLink() const {if (!d)return false;return d->isSymLink; }/*!Returns true if the URL is writable; otherwise returns false.\sa isValid() */bool QUrlInfo::isWritable() const {if (!d)return false;return d->isWritable; }/*!Returns true if the URL is readable; otherwise returns false.\sa isValid() */bool QUrlInfo::isReadable() const {if (!d)return false;return d->isReadable; }/*!Returns true if the URL is executable; otherwise returns false.\sa isValid() */bool QUrlInfo::isExecutable() const {if (!d)return false;return d->isExecutable; }/*!Returns true if \a i1 is greater than \a i2; otherwise returnsfalse. The objects are compared by the value, which is specifiedby \a sortBy. This must be one of QDir::Name, QDir::Time orQDir::Size. */bool QUrlInfo::greaterThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy) {switch (sortBy) {case QDir::Name:return i1.name() > i2.name();case QDir::Time:return i1.lastModified() > i2.lastModified();case QDir::Size:return i1.size() > i2.size();default:return false;} }/*!Returns true if \a i1 is less than \a i2; otherwise returns false.The objects are compared by the value, which is specified by \asortBy. This must be one of QDir::Name, QDir::Time or QDir::Size. */bool QUrlInfo::lessThan(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy) {return !greaterThan(i1, i2, sortBy); }/*!Returns true if \a i1 equals to \a i2; otherwise returns false.The objects are compared by the value, which is specified by \asortBy. This must be one of QDir::Name, QDir::Time or QDir::Size. */bool QUrlInfo::equal(const QUrlInfo &i1, const QUrlInfo &i2,int sortBy) {switch (sortBy) {case QDir::Name:return i1.name() == i2.name();case QDir::Time:return i1.lastModified() == i2.lastModified();case QDir::Size:return i1.size() == i2.size();default:return false;} }/*!Returns true if this QUrlInfo is equal to \a other; otherwisereturns false.\sa lessThan(), equal() */bool QUrlInfo::operator==(const QUrlInfo &other) const {if (!d)return other.d == 0;if (!other.d)return false;return (d->name == other.d->name &&d->permissions == other.d->permissions &&d->owner == other.d->owner &&d->group == other.d->group &&d->size == other.d->size &&d->lastModified == other.d->lastModified &&d->lastRead == other.d->lastRead &&d->isDir == other.d->isDir &&d->isFile == other.d->isFile &&d->isSymLink == other.d->isSymLink &&d->isWritable == other.d->isWritable &&d->isReadable == other.d->isReadable &&d->isExecutable == other.d->isExecutable); }/*!\fn bool QUrlInfo::operator!=(const QUrlInfo &other) const\since 4.2Returns true if this QUrlInfo is not equal to \a other; otherwisereturns false.\sa lessThan(), equal() *//*!Returns true if the URL info is valid; otherwise returns false.Valid means that the QUrlInfo contains real information.You should always check if the URL info is valid before relying onthe values. */ bool QUrlInfo::isValid() const {return d != 0; }QT_END_NAMESPACE參考文獻:霍亞飛. Qt Creator快速入門[M](第三版). 北京航空航天大學出版社, 2017.
資料:鏈接:https://pan.baidu.com/s/1eN2RKoWAC6mAcEXsWeBB_w 密碼:2koh
總結
- 上一篇: 重学java基础第九课:软件和软件关系
- 下一篇: [Redux/Mobx] Redux中异