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"