File indexing completed on 2024-04-14 03:47:46

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
0005 // SPDX-FileCopyrightText: 2008, 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
0006 //
0007 
0008 #include "HttpDownloadManager.h"
0009 
0010 #include <QList>
0011 #include <QMap>
0012 #include <QTimer>
0013 #include <QNetworkAccessManager>
0014 #include <QCoreApplication>
0015 
0016 #include "DownloadPolicy.h"
0017 #include "DownloadQueueSet.h"
0018 #include "HttpJob.h"
0019 #include "MarbleDebug.h"
0020 #include "StoragePolicy.h"
0021 
0022 using namespace Marble;
0023 
0024 // Time before a failed download job is requeued in ms
0025 const quint32 requeueTime = 60000;
0026 
0027 class Q_DECL_HIDDEN HttpDownloadManager::Private
0028 {
0029   public:
0030     Private( HttpDownloadManager* parent, StoragePolicy *policy );
0031     ~Private();
0032 
0033     void connectDefaultQueueSets();
0034     void connectQueueSet( DownloadQueueSet * );
0035     bool hasDownloadPolicy( const DownloadPolicy& policy ) const;
0036     void finishJob( const QByteArray&, const QString&, const QString& id );
0037     void requeue();
0038     void startRetryTimer();
0039 
0040     DownloadQueueSet *findQueues( const QString& hostName, const DownloadUsage usage );
0041 
0042     HttpDownloadManager* m_downloadManager;
0043     QTimer m_requeueTimer;
0044     /**
0045      * Contains per download policy a queue set containing of
0046      * - a queue where jobs are waiting for being activated (=downloaded)
0047      * - a queue containing currently being downloaded
0048      * - a queue for retries of failed downloads */
0049     QList<QPair<DownloadPolicyKey, DownloadQueueSet *> > m_queueSets;
0050     QMap<DownloadUsage, DownloadQueueSet *> m_defaultQueueSets;
0051     StoragePolicy *const m_storagePolicy;
0052     QNetworkAccessManager m_networkAccessManager;
0053     bool m_acceptJobs;
0054 
0055 };
0056 
0057 HttpDownloadManager::Private::Private(HttpDownloadManager *parent, StoragePolicy *policy )
0058     : m_downloadManager( parent ),
0059       m_requeueTimer(),
0060       m_storagePolicy( policy ),
0061       m_networkAccessManager(),
0062       m_acceptJobs( true )
0063 {
0064     // setup default download policy and associated queue set
0065     DownloadPolicy defaultBrowsePolicy;
0066     defaultBrowsePolicy.setMaximumConnections( 20 );
0067     m_defaultQueueSets[ DownloadBrowse ] = new DownloadQueueSet( defaultBrowsePolicy );
0068     DownloadPolicy defaultBulkDownloadPolicy;
0069     defaultBulkDownloadPolicy.setMaximumConnections( 2 );
0070     m_defaultQueueSets[ DownloadBulk ] = new DownloadQueueSet( defaultBulkDownloadPolicy );
0071 }
0072 
0073 HttpDownloadManager::Private::~Private()
0074 {
0075     QMap<DownloadUsage, DownloadQueueSet *>::iterator pos = m_defaultQueueSets.begin();
0076     QMap<DownloadUsage, DownloadQueueSet *>::iterator const end = m_defaultQueueSets.end();
0077     for (; pos != end; ++pos )
0078         delete pos.value();
0079 }
0080 
0081 DownloadQueueSet *HttpDownloadManager::Private::findQueues( const QString& hostName,
0082                                                             const DownloadUsage usage )
0083 {
0084     DownloadQueueSet * result = nullptr;
0085     QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::iterator pos = m_queueSets.begin();
0086     QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::iterator const end = m_queueSets.end();
0087     for (; pos != end; ++pos ) {
0088         if ( (*pos).first.matches( hostName, usage )) {
0089             result = (*pos).second;
0090             break;
0091         }
0092     }
0093     if ( !result ) {
0094         mDebug() << "No download policy found for" << hostName << usage
0095                  << ", using default policy.";
0096         result = m_defaultQueueSets[ usage ];
0097     }
0098     return result;
0099 }
0100 
0101 
0102 HttpDownloadManager::HttpDownloadManager( StoragePolicy *policy )
0103     : d( new Private( this, policy ) )
0104 {
0105     d->m_requeueTimer.setInterval( requeueTime );
0106     connect( &d->m_requeueTimer, SIGNAL(timeout()), this, SLOT(requeue()) );
0107     d->connectDefaultQueueSets();
0108 }
0109 
0110 HttpDownloadManager::~HttpDownloadManager()
0111 {
0112     delete d;
0113 }
0114 
0115 void HttpDownloadManager::setDownloadEnabled( const bool enable )
0116 {
0117     d->m_networkAccessManager.setNetworkAccessible( enable ? QNetworkAccessManager::Accessible : QNetworkAccessManager::NotAccessible );
0118     d->m_acceptJobs = enable;
0119     QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator pos = d->m_queueSets.begin();
0120     QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator const end = d->m_queueSets.end();
0121     for (; pos != end; ++pos ) {
0122         pos->second->purgeJobs();
0123     }
0124 
0125 }
0126 
0127 void HttpDownloadManager::addDownloadPolicy( const DownloadPolicy& policy )
0128 {
0129     if ( d->hasDownloadPolicy( policy ))
0130         return;
0131     DownloadQueueSet * const queueSet = new DownloadQueueSet( policy, this );
0132     d->connectQueueSet( queueSet );
0133     d->m_queueSets.append( QPair<DownloadPolicyKey, DownloadQueueSet *>
0134                            ( queueSet->downloadPolicy().key(), queueSet ));
0135 }
0136 
0137 void HttpDownloadManager::addJob( const QUrl& sourceUrl, const QString& destFileName,
0138                                   const QString &id, const DownloadUsage usage )
0139 {
0140     if ( !d->m_acceptJobs ) {
0141         mDebug() << "Working offline, not adding job";
0142         return;
0143     }
0144 
0145     DownloadQueueSet * const queueSet = d->findQueues( sourceUrl.host(), usage );
0146     if ( queueSet->canAcceptJob( sourceUrl, destFileName )) {
0147         HttpJob * const job = new HttpJob( sourceUrl, destFileName, id, &d->m_networkAccessManager );
0148         job->setUserAgentPluginId( "QNamNetworkPlugin" );
0149         job->setDownloadUsage( usage );
0150         mDebug() << "adding job " << sourceUrl;
0151         queueSet->addJob( job );
0152     }
0153 }
0154 
0155 void HttpDownloadManager::Private::finishJob( const QByteArray& data, const QString& destinationFileName,
0156                                      const QString& id )
0157 {
0158     mDebug() << "emitting downloadComplete( QByteArray, " << id << ")";
0159     emit m_downloadManager->downloadComplete( data, id );
0160     if ( m_storagePolicy ) {
0161         const bool saved = m_storagePolicy->updateFile( destinationFileName, data );
0162         if ( saved ) {
0163             mDebug() << "emitting downloadComplete( " << destinationFileName << ", " << id << ")";
0164             emit m_downloadManager->downloadComplete( destinationFileName, id );
0165         } else {
0166             qWarning() << "Could not save:" << destinationFileName;
0167         }
0168     }
0169 }
0170 
0171 void HttpDownloadManager::Private::requeue()
0172 {
0173     m_requeueTimer.stop();
0174 
0175     QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator pos = m_queueSets.begin();
0176     QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator const end = m_queueSets.end();
0177     for (; pos != end; ++pos ) {
0178         (*pos).second->retryJobs();
0179     }
0180 }
0181 
0182 void HttpDownloadManager::Private::startRetryTimer()
0183 {
0184     if ( !m_requeueTimer.isActive() )
0185         m_requeueTimer.start();
0186 }
0187 
0188 void HttpDownloadManager::Private::connectDefaultQueueSets()
0189 {
0190     QMap<DownloadUsage, DownloadQueueSet *>::iterator pos = m_defaultQueueSets.begin();
0191     QMap<DownloadUsage, DownloadQueueSet *>::iterator const end = m_defaultQueueSets.end();
0192     for (; pos != end; ++pos )
0193         connectQueueSet( pos.value() );
0194 }
0195 
0196 void HttpDownloadManager::Private::connectQueueSet( DownloadQueueSet * queueSet )
0197 {
0198     connect( queueSet, SIGNAL(jobFinished(QByteArray,QString,QString)),
0199              m_downloadManager, SLOT(finishJob(QByteArray,QString,QString)));
0200     connect( queueSet, SIGNAL(jobRetry()), m_downloadManager, SLOT(startRetryTimer()));
0201     connect( queueSet, SIGNAL(jobRedirected(QUrl,QString,QString,DownloadUsage)),
0202              m_downloadManager, SLOT(addJob(QUrl,QString,QString,DownloadUsage)));
0203     // relay jobAdded/jobRemoved signals (interesting for progress bar)
0204     connect( queueSet, SIGNAL(jobAdded()), m_downloadManager, SIGNAL(jobAdded()));
0205     connect( queueSet, SIGNAL(jobRemoved()), m_downloadManager, SIGNAL(jobRemoved()));
0206     connect( queueSet, SIGNAL(progressChanged(int,int)), m_downloadManager, SIGNAL(progressChanged(int,int)) );
0207 }
0208 
0209 bool HttpDownloadManager::Private::hasDownloadPolicy( const DownloadPolicy& policy ) const
0210 {
0211     bool found = false;
0212     QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::const_iterator pos = m_queueSets.constBegin();
0213     QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::const_iterator const end = m_queueSets.constEnd();
0214     for (; pos != end; ++pos ) {
0215         if ( (*pos).second->downloadPolicy() == policy ) {
0216             found = true;
0217             break;
0218         }
0219     }
0220     return found;
0221 }
0222 
0223 QByteArray HttpDownloadManager::userAgent(const QString &platform, const QString &component)
0224 {
0225     QString result( "Mozilla/5.0 (compatible; Marble/%1; %2; %3; %4; %5)" );
0226     bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
0227     QString const device = smallScreen ? "MobileDevice" : "DesktopDevice";
0228     QString const app = QCoreApplication::applicationName();
0229     result = result.arg( MARBLE_VERSION_STRING, device, platform, component, app);
0230     return result.toLatin1();
0231 }
0232 
0233 #include "moc_HttpDownloadManager.cpp"