~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/network/qftp.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

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