source: trunk/src/network/access/qnetworkaccessftpbackend.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 11.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qnetworkaccessftpbackend_p.h"
43#include "qnetworkaccessmanager_p.h"
44#include "QtNetwork/qauthenticator.h"
45#include "private/qnoncontiguousbytedevice_p.h"
46
47#ifndef QT_NO_FTP
48
49QT_BEGIN_NAMESPACE
50
51enum {
52 DefaultFtpPort = 21
53};
54
55static QByteArray makeCacheKey(const QUrl &url)
56{
57 QUrl copy = url;
58 copy.setPort(url.port(DefaultFtpPort));
59 return "ftp-connection:" +
60 copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery |
61 QUrl::RemoveFragment);
62}
63
64QNetworkAccessBackend *
65QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
66 const QNetworkRequest &request) const
67{
68 // is it an operation we know of?
69 switch (op) {
70 case QNetworkAccessManager::GetOperation:
71 case QNetworkAccessManager::PutOperation:
72 break;
73
74 default:
75 // no, we can't handle this operation
76 return 0;
77 }
78
79 QUrl url = request.url();
80 if (url.scheme() == QLatin1String("ftp"))
81 return new QNetworkAccessFtpBackend;
82 return 0;
83}
84
85class QNetworkAccessCachedFtpConnection: public QFtp, public QNetworkAccessCache::CacheableObject
86{
87 // Q_OBJECT
88public:
89 QNetworkAccessCachedFtpConnection()
90 {
91 setExpires(true);
92 setShareable(false);
93 }
94
95 void dispose()
96 {
97 connect(this, SIGNAL(done(bool)), this, SLOT(deleteLater()));
98 close();
99 }
100};
101
102QNetworkAccessFtpBackend::QNetworkAccessFtpBackend()
103 : ftp(0), uploadDevice(0), totalBytes(0), helpId(-1), sizeId(-1), mdtmId(-1),
104 supportsSize(false), supportsMdtm(false), state(Idle)
105{
106}
107
108QNetworkAccessFtpBackend::~QNetworkAccessFtpBackend()
109{
110 disconnectFromFtp();
111}
112
113void QNetworkAccessFtpBackend::open()
114{
115#ifndef QT_NO_NETWORKPROXY
116 QNetworkProxy proxy;
117 foreach (const QNetworkProxy &p, proxyList()) {
118 // use the first FTP proxy
119 // or no proxy at all
120 if (p.type() == QNetworkProxy::FtpCachingProxy
121 || p.type() == QNetworkProxy::NoProxy) {
122 proxy = p;
123 break;
124 }
125 }
126
127 // did we find an FTP proxy or a NoProxy?
128 if (proxy.type() == QNetworkProxy::DefaultProxy) {
129 // unsuitable proxies
130 error(QNetworkReply::ProxyNotFoundError,
131 tr("No suitable proxy found"));
132 finished();
133 return;
134 }
135
136#endif
137
138 QUrl url = this->url();
139 if (url.path().isEmpty()) {
140 url.setPath(QLatin1String("/"));
141 setUrl(url);
142 }
143 if (url.path().endsWith(QLatin1Char('/'))) {
144 error(QNetworkReply::ContentOperationNotPermittedError,
145 tr("Cannot open %1: is a directory").arg(url.toString()));
146 finished();
147 return;
148 }
149 state = LoggingIn;
150
151 QNetworkAccessCache* objectCache = QNetworkAccessManagerPrivate::getObjectCache(this);
152 QByteArray cacheKey = makeCacheKey(url);
153 if (!objectCache->requestEntry(cacheKey, this,
154 SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) {
155 ftp = new QNetworkAccessCachedFtpConnection;
156#ifndef QT_NO_NETWORKPROXY
157 if (proxy.type() == QNetworkProxy::FtpCachingProxy)
158 ftp->setProxy(proxy.hostName(), proxy.port());
159#endif
160 ftp->connectToHost(url.host(), url.port(DefaultFtpPort));
161 ftp->login(url.userName(), url.password());
162
163 objectCache->addEntry(cacheKey, ftp);
164 ftpConnectionReady(ftp);
165 }
166
167 // Put operation
168 if (operation() == QNetworkAccessManager::PutOperation) {
169 uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice());
170 uploadDevice->setParent(this);
171 }
172}
173
174void QNetworkAccessFtpBackend::closeDownstreamChannel()
175{
176 state = Disconnecting;
177 if (operation() == QNetworkAccessManager::GetOperation)
178#ifndef Q_OS_WINCE
179 abort();
180#else
181 exit(3);
182#endif
183}
184
185void QNetworkAccessFtpBackend::downstreamReadyWrite()
186{
187 if (state == Transferring && ftp && ftp->bytesAvailable())
188 ftpReadyRead();
189}
190
191void QNetworkAccessFtpBackend::ftpConnectionReady(QNetworkAccessCache::CacheableObject *o)
192{
193 ftp = static_cast<QNetworkAccessCachedFtpConnection *>(o);
194 connect(ftp, SIGNAL(done(bool)), SLOT(ftpDone()));
195 connect(ftp, SIGNAL(rawCommandReply(int,QString)), SLOT(ftpRawCommandReply(int,QString)));
196 connect(ftp, SIGNAL(readyRead()), SLOT(ftpReadyRead()));
197
198 // is the login process done already?
199 if (ftp->state() == QFtp::LoggedIn)
200 ftpDone();
201
202 // no, defer the actual operation until after we've logged in
203}
204
205void QNetworkAccessFtpBackend::disconnectFromFtp()
206{
207 state = Disconnecting;
208
209 if (ftp) {
210 disconnect(ftp, 0, this, 0);
211
212 QByteArray key = makeCacheKey(url());
213 QNetworkAccessManagerPrivate::getObjectCache(this)->releaseEntry(key);
214
215 ftp = 0;
216 }
217}
218
219void QNetworkAccessFtpBackend::ftpDone()
220{
221 // the last command we sent is done
222 if (state == LoggingIn && ftp->state() != QFtp::LoggedIn) {
223 if (ftp->state() == QFtp::Connected) {
224 // the login did not succeed
225 QUrl newUrl = url();
226 newUrl.setUserInfo(QString());
227 setUrl(newUrl);
228
229 QAuthenticator auth;
230 authenticationRequired(&auth);
231
232 if (!auth.isNull()) {
233 // try again:
234 newUrl.setUserName(auth.user());
235 ftp->login(auth.user(), auth.password());
236 return;
237 }
238
239 error(QNetworkReply::AuthenticationRequiredError,
240 tr("Logging in to %1 failed: authentication required")
241 .arg(url().host()));
242 } else {
243 // we did not connect
244 QNetworkReply::NetworkError code;
245 switch (ftp->error()) {
246 case QFtp::HostNotFound:
247 code = QNetworkReply::HostNotFoundError;
248 break;
249
250 case QFtp::ConnectionRefused:
251 code = QNetworkReply::ConnectionRefusedError;
252 break;
253
254 default:
255 code = QNetworkReply::ProtocolFailure;
256 break;
257 }
258
259 error(code, ftp->errorString());
260 }
261
262 // we're not connected, so remove the cache entry:
263 QByteArray key = makeCacheKey(url());
264 QNetworkAccessManagerPrivate::getObjectCache(this)->removeEntry(key);
265
266 disconnect(ftp, 0, this, 0);
267 ftp->dispose();
268 ftp = 0;
269
270 state = Disconnecting;
271 finished();
272 return;
273 }
274
275 // check for errors:
276 if (ftp->error() != QFtp::NoError) {
277 QString msg;
278 if (operation() == QNetworkAccessManager::GetOperation)
279 msg = tr("Error while downloading %1: %2");
280 else
281 msg = tr("Error while uploading %1: %2");
282 msg = msg.arg(url().toString(), ftp->errorString());
283
284 if (state == Statting)
285 // file probably doesn't exist
286 error(QNetworkReply::ContentNotFoundError, msg);
287 else
288 error(QNetworkReply::ContentAccessDenied, msg);
289
290 disconnectFromFtp();
291 finished();
292 }
293
294 if (state == LoggingIn) {
295 state = CheckingFeatures;
296 if (operation() == QNetworkAccessManager::GetOperation) {
297 // send help command to find out if server supports "SIZE" and "MDTM"
298 QString command = url().path();
299 command.prepend(QLatin1String("%1 "));
300 helpId = ftp->rawCommand(QLatin1String("HELP")); // get supported commands
301 } else {
302 ftpDone();
303 }
304 } else if (state == CheckingFeatures) {
305 state = Statting;
306 if (operation() == QNetworkAccessManager::GetOperation) {
307 // logged in successfully, send the stat requests (if supported)
308 QString command = url().path();
309 command.prepend(QLatin1String("%1 "));
310 if (supportsSize)
311 sizeId = ftp->rawCommand(command.arg(QLatin1String("SIZE"))); // get size
312 if (supportsMdtm)
313 mdtmId = ftp->rawCommand(command.arg(QLatin1String("MDTM"))); // get modified time
314 if (!supportsSize && !supportsMdtm)
315 ftpDone(); // no commands sent, move to the next state
316 } else {
317 ftpDone();
318 }
319 } else if (state == Statting) {
320 // statted successfully, send the actual request
321 emit metaDataChanged();
322 state = Transferring;
323
324 QFtp::TransferType type = QFtp::Binary;
325 if (operation() == QNetworkAccessManager::GetOperation) {
326 setCachingEnabled(true);
327 ftp->get(url().path(), 0, type);
328 } else {
329 ftp->put(uploadDevice, url().path(), type);
330 }
331
332 } else if (state == Transferring) {
333 // upload or download finished
334 disconnectFromFtp();
335 finished();
336 }
337}
338
339void QNetworkAccessFtpBackend::ftpReadyRead()
340{
341 QByteArray data = ftp->readAll();
342 QByteDataBuffer list;
343 list.append(data);
344 data.clear(); // important because of implicit sharing!
345 writeDownstreamData(list);
346}
347
348void QNetworkAccessFtpBackend::ftpRawCommandReply(int code, const QString &text)
349{
350 //qDebug() << "FTP reply:" << code << text;
351 int id = ftp->currentId();
352
353 if ((id == helpId) && ((code == 200) || (code == 214))) { // supported commands
354 // the "FEAT" ftp command would be nice here, but it is not part of the
355 // initial FTP RFC 959, neither ar "SIZE" nor "MDTM" (they are all specified
356 // in RFC 3659)
357 if (text.contains(QLatin1String("SIZE"), Qt::CaseSensitive))
358 supportsSize = true;
359 if (text.contains(QLatin1String("MDTM"), Qt::CaseSensitive))
360 supportsMdtm = true;
361 } else if (code == 213) { // file status
362 if (id == sizeId) {
363 // reply to the size command
364 setHeader(QNetworkRequest::ContentLengthHeader, text.toLongLong());
365#ifndef QT_NO_DATESTRING
366 } else if (id == mdtmId) {
367 QDateTime dt = QDateTime::fromString(text, QLatin1String("yyyyMMddHHmmss"));
368 setHeader(QNetworkRequest::LastModifiedHeader, dt);
369#endif
370 }
371 }
372}
373
374QT_END_NAMESPACE
375
376#endif // QT_NO_FTP
Note: See TracBrowser for help on using the repository browser.