Details
-
Bug
-
Resolution: Done
-
P2: Important
-
4.6.3
-
None
-
Qt-embedded for Linux
Qt SDK 4.6.3 for Windows
-
3dbf98c715ed6941ca41eb85981a6afccab489a6
Description
When FTP login fails, QNetworkAccessFtpBackend changes the key for QNetworkAccessCache. This causes an error message to appear, and the code to segfault when the NAM object is deleted. Furthermore, it seems there is no reason for this change. When the library was patched to prevent this change, no adverse effect was observed.
Error message example
QNetworkAccessCache::removeEntry: trying to remove key 'ftp-connection:ftp://192.168.1.1:21' that is not in cache
To reproduce this problem, start a network request with a URL that has a username.
QNetworkRequest example
QUrl url("ftp://test_user:test_password@192.168.1.1/pub/test_file"); QNetworkAccessManager * p_nam = new QNetworkAccessManager(this); QNetworkReply * p_reply = p_nam->get(QNetworkRequest(url));
When the FTP connection is opened, the user name becomes part of the hash key.
- Only the password is removed — copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery |
src/network/access/qnetworkaccessftpbackend.cppstatic QByteArray makeCacheKey(const QUrl &url) { QUrl copy = url; copy.setPort(url.port(DefaultFtpPort)); return "ftp-connection:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment); } void QNetworkAccessFtpBackend::open() { #ifndef QT_NO_NETWORKPROXY QNetworkProxy proxy; foreach (const QNetworkProxy &p, proxyList()) { // use the first FTP proxy // or no proxy at all if (p.type() == QNetworkProxy::FtpCachingProxy || p.type() == QNetworkProxy::NoProxy) { proxy = p; break; } } // did we find an FTP proxy or a NoProxy? if (proxy.type() == QNetworkProxy::DefaultProxy) { // unsuitable proxies error(QNetworkReply::ProxyNotFoundError, tr("No suitable proxy found")); finished(); return; } #endif QUrl url = this->url(); if (url.path().isEmpty()) { url.setPath(QLatin1String("/")); setUrl(url); } if (url.path().endsWith(QLatin1Char('/'))) { error(QNetworkReply::ContentOperationNotPermittedError, tr("Cannot open %1: is a directory").arg(url.toString())); finished(); return; } state = LoggingIn; QNetworkAccessCache* objectCache = QNetworkAccessManagerPrivate::getObjectCache(this); QByteArray cacheKey = makeCacheKey(url); if (!objectCache->requestEntry(cacheKey, this, SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) { ftp = new QNetworkAccessCachedFtpConnection; #ifndef QT_NO_NETWORKPROXY if (proxy.type() == QNetworkProxy::FtpCachingProxy) ftp->setProxy(proxy.hostName(), proxy.port()); #endif ftp->connectToHost(url.host(), url.port(DefaultFtpPort)); ftp->login(url.userName(), url.password()); objectCache->addEntry(cacheKey, ftp); ftpConnectionReady(ftp); } // Put operation if (operation() == QNetworkAccessManager::PutOperation) { uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice()); uploadDevice->setParent(this); } }
However, when login fails, ftpDone functions deletes the username and the password, and this alters the hash key to access the cache - QNetworkAccessFtpBackend makes a copy of the URL — QUrl newUrl = url()
- sets the username in that URL to a empty string — newUrl.setUserInfo(QString());
- finally sets the URL of the reply to the new URL. — setUrl(newUrl);
- make a hash key from this new URL — QByteArray key = makeCacheKey(url())
src/network/access/qnetworkaccessftpbackend.cppvoid QNetworkAccessFtpBackend::ftpDone() { // the last command we sent is done if (state == LoggingIn && ftp->state() != QFtp::LoggedIn) { if (ftp->state() == QFtp::Connected) { // the login did not succeed QUrl newUrl = url(); newUrl.setUserInfo(QString()); setUrl(newUrl); QAuthenticator auth; authenticationRequired(&auth); if (!auth.isNull()) { // try again: newUrl.setUserName(auth.user()); ftp->login(auth.user(), auth.password()); return; } error(QNetworkReply::AuthenticationRequiredError, tr("Logging in to %1 failed: authentication required") .arg(url().host())); } else { // we did not connect QNetworkReply::NetworkError code; switch (ftp->error()) { case QFtp::HostNotFound: code = QNetworkReply::HostNotFoundError; break; case QFtp::ConnectionRefused: code = QNetworkReply::ConnectionRefusedError; break; default: code = QNetworkReply::ProtocolFailure; break; } error(code, ftp->errorString()); } // we're not connected, so remove the cache entry: QByteArray key = makeCacheKey(url()); QNetworkAccessManagerPrivate::getObjectCache(this)->removeEntry(key); disconnect(ftp, 0, this, 0); ftp->dispose(); ftp = 0; state = Disconnecting; finished(); return; }