File indexing completed on 2025-01-05 03:59:36

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