Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-11824

QNetworkAccessFtpBackend changes key for accessing QNetworkAccessCache

    XMLWordPrintable

Details

    • 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.cpp
        static 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.cpp
        void 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;
            }
        
        

      Attachments

        Activity

          People

            xcm Martin Petersson (Inactive)
            macbruins William Liao
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: