qftp.cpp 77 KB

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