1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
4 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5 // SPDX-FileCopyrightText: 2008, 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
6 //
7
8 #include "HttpDownloadManager.h"
9
10 #include <QList>
11 #include <QMap>
12 #include <QTimer>
13 #include <QNetworkAccessManager>
14
15 #include "DownloadPolicy.h"
16 #include "DownloadQueueSet.h"
17 #include "HttpJob.h"
18 #include "MarbleDebug.h"
19 #include "StoragePolicy.h"
20
21 using namespace Marble;
22
23 // Time before a failed download job is requeued in ms
24 const quint32 requeueTime = 60000;
25
26 class Q_DECL_HIDDEN HttpDownloadManager::Private
27 {
28 public:
29 Private( HttpDownloadManager* parent, StoragePolicy *policy );
30 ~Private();
31
32 void connectDefaultQueueSets();
33 void connectQueueSet( DownloadQueueSet * );
34 bool hasDownloadPolicy( const DownloadPolicy& policy ) const;
35 void finishJob( const QByteArray&, const QString&, const QString& id );
36 void requeue();
37 void startRetryTimer();
38
39 DownloadQueueSet *findQueues( const QString& hostName, const DownloadUsage usage );
40
41 HttpDownloadManager* m_downloadManager;
42 QTimer m_requeueTimer;
43 /**
44 * Contains per download policy a queue set containing of
45 * - a queue where jobs are waiting for being activated (=downloaded)
46 * - a queue containing currently being downloaded
47 * - a queue for retries of failed downloads */
48 QList<QPair<DownloadPolicyKey, DownloadQueueSet *> > m_queueSets;
49 QMap<DownloadUsage, DownloadQueueSet *> m_defaultQueueSets;
50 StoragePolicy *const m_storagePolicy;
51 QNetworkAccessManager m_networkAccessManager;
52 bool m_acceptJobs;
53
54 };
55
Private(HttpDownloadManager * parent,StoragePolicy * policy)56 HttpDownloadManager::Private::Private(HttpDownloadManager *parent, StoragePolicy *policy )
57 : m_downloadManager( parent ),
58 m_requeueTimer(),
59 m_storagePolicy( policy ),
60 m_networkAccessManager(),
61 m_acceptJobs( true )
62 {
63 // setup default download policy and associated queue set
64 DownloadPolicy defaultBrowsePolicy;
65 defaultBrowsePolicy.setMaximumConnections( 20 );
66 m_defaultQueueSets[ DownloadBrowse ] = new DownloadQueueSet( defaultBrowsePolicy );
67 DownloadPolicy defaultBulkDownloadPolicy;
68 defaultBulkDownloadPolicy.setMaximumConnections( 2 );
69 m_defaultQueueSets[ DownloadBulk ] = new DownloadQueueSet( defaultBulkDownloadPolicy );
70 }
71
~Private()72 HttpDownloadManager::Private::~Private()
73 {
74 QMap<DownloadUsage, DownloadQueueSet *>::iterator pos = m_defaultQueueSets.begin();
75 QMap<DownloadUsage, DownloadQueueSet *>::iterator const end = m_defaultQueueSets.end();
76 for (; pos != end; ++pos )
77 delete pos.value();
78 }
79
findQueues(const QString & hostName,const DownloadUsage usage)80 DownloadQueueSet *HttpDownloadManager::Private::findQueues( const QString& hostName,
81 const DownloadUsage usage )
82 {
83 DownloadQueueSet * result = nullptr;
84 QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::iterator pos = m_queueSets.begin();
85 QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::iterator const end = m_queueSets.end();
86 for (; pos != end; ++pos ) {
87 if ( (*pos).first.matches( hostName, usage )) {
88 result = (*pos).second;
89 break;
90 }
91 }
92 if ( !result ) {
93 mDebug() << "No download policy found for" << hostName << usage
94 << ", using default policy.";
95 result = m_defaultQueueSets[ usage ];
96 }
97 return result;
98 }
99
100
HttpDownloadManager(StoragePolicy * policy)101 HttpDownloadManager::HttpDownloadManager( StoragePolicy *policy )
102 : d( new Private( this, policy ) )
103 {
104 d->m_requeueTimer.setInterval( requeueTime );
105 connect( &d->m_requeueTimer, SIGNAL(timeout()), this, SLOT(requeue()) );
106 d->connectDefaultQueueSets();
107 }
108
~HttpDownloadManager()109 HttpDownloadManager::~HttpDownloadManager()
110 {
111 delete d;
112 }
113
setDownloadEnabled(const bool enable)114 void HttpDownloadManager::setDownloadEnabled( const bool enable )
115 {
116 d->m_networkAccessManager.setNetworkAccessible( enable ? QNetworkAccessManager::Accessible : QNetworkAccessManager::NotAccessible );
117 d->m_acceptJobs = enable;
118 QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator pos = d->m_queueSets.begin();
119 QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator const end = d->m_queueSets.end();
120 for (; pos != end; ++pos ) {
121 pos->second->purgeJobs();
122 }
123
124 }
125
addDownloadPolicy(const DownloadPolicy & policy)126 void HttpDownloadManager::addDownloadPolicy( const DownloadPolicy& policy )
127 {
128 if ( d->hasDownloadPolicy( policy ))
129 return;
130 DownloadQueueSet * const queueSet = new DownloadQueueSet( policy, this );
131 d->connectQueueSet( queueSet );
132 d->m_queueSets.append( QPair<DownloadPolicyKey, DownloadQueueSet *>
133 ( queueSet->downloadPolicy().key(), queueSet ));
134 }
135
addJob(const QUrl & sourceUrl,const QString & destFileName,const QString & id,const DownloadUsage usage)136 void HttpDownloadManager::addJob( const QUrl& sourceUrl, const QString& destFileName,
137 const QString &id, const DownloadUsage usage )
138 {
139 if ( !d->m_acceptJobs ) {
140 mDebug() << Q_FUNC_INFO << "Working offline, not adding job";
141 return;
142 }
143
144 DownloadQueueSet * const queueSet = d->findQueues( sourceUrl.host(), usage );
145 if ( queueSet->canAcceptJob( sourceUrl, destFileName )) {
146 HttpJob * const job = new HttpJob( sourceUrl, destFileName, id, &d->m_networkAccessManager );
147 job->setUserAgentPluginId( "QNamNetworkPlugin" );
148 job->setDownloadUsage( usage );
149 mDebug() << "adding job " << sourceUrl;
150 queueSet->addJob( job );
151 }
152 }
153
finishJob(const QByteArray & data,const QString & destinationFileName,const QString & id)154 void HttpDownloadManager::Private::finishJob( const QByteArray& data, const QString& destinationFileName,
155 const QString& id )
156 {
157 mDebug() << "emitting downloadComplete( QByteArray, " << id << ")";
158 emit m_downloadManager->downloadComplete( data, id );
159 if ( m_storagePolicy ) {
160 const bool saved = m_storagePolicy->updateFile( destinationFileName, data );
161 if ( saved ) {
162 mDebug() << "emitting downloadComplete( " << destinationFileName << ", " << id << ")";
163 emit m_downloadManager->downloadComplete( destinationFileName, id );
164 } else {
165 qWarning() << "Could not save:" << destinationFileName;
166 }
167 }
168 }
169
requeue()170 void HttpDownloadManager::Private::requeue()
171 {
172 m_requeueTimer.stop();
173
174 QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator pos = m_queueSets.begin();
175 QList<QPair<DownloadPolicyKey, DownloadQueueSet *> >::iterator const end = m_queueSets.end();
176 for (; pos != end; ++pos ) {
177 (*pos).second->retryJobs();
178 }
179 }
180
startRetryTimer()181 void HttpDownloadManager::Private::startRetryTimer()
182 {
183 if ( !m_requeueTimer.isActive() )
184 m_requeueTimer.start();
185 }
186
connectDefaultQueueSets()187 void HttpDownloadManager::Private::connectDefaultQueueSets()
188 {
189 QMap<DownloadUsage, DownloadQueueSet *>::iterator pos = m_defaultQueueSets.begin();
190 QMap<DownloadUsage, DownloadQueueSet *>::iterator const end = m_defaultQueueSets.end();
191 for (; pos != end; ++pos )
192 connectQueueSet( pos.value() );
193 }
194
connectQueueSet(DownloadQueueSet * queueSet)195 void HttpDownloadManager::Private::connectQueueSet( DownloadQueueSet * queueSet )
196 {
197 connect( queueSet, SIGNAL(jobFinished(QByteArray,QString,QString)),
198 m_downloadManager, SLOT(finishJob(QByteArray,QString,QString)));
199 connect( queueSet, SIGNAL(jobRetry()), m_downloadManager, SLOT(startRetryTimer()));
200 connect( queueSet, SIGNAL(jobRedirected(QUrl,QString,QString,DownloadUsage)),
201 m_downloadManager, SLOT(addJob(QUrl,QString,QString,DownloadUsage)));
202 // relay jobAdded/jobRemoved signals (interesting for progress bar)
203 connect( queueSet, SIGNAL(jobAdded()), m_downloadManager, SIGNAL(jobAdded()));
204 connect( queueSet, SIGNAL(jobRemoved()), m_downloadManager, SIGNAL(jobRemoved()));
205 connect( queueSet, SIGNAL(progressChanged(int,int)), m_downloadManager, SIGNAL(progressChanged(int,int)) );
206 }
207
hasDownloadPolicy(const DownloadPolicy & policy) const208 bool HttpDownloadManager::Private::hasDownloadPolicy( const DownloadPolicy& policy ) const
209 {
210 bool found = false;
211 QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::const_iterator pos = m_queueSets.constBegin();
212 QList<QPair<DownloadPolicyKey, DownloadQueueSet*> >::const_iterator const end = m_queueSets.constEnd();
213 for (; pos != end; ++pos ) {
214 if ( (*pos).second->downloadPolicy() == policy ) {
215 found = true;
216 break;
217 }
218 }
219 return found;
220 }
221
userAgent(const QString & platform,const QString & component)222 QByteArray HttpDownloadManager::userAgent(const QString &platform, const QString &component)
223 {
224 QString result( "Mozilla/5.0 (compatible; Marble/%1; %2; %3; %4)" );
225 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
226 QString const device = smallScreen ? "MobileDevice" : "DesktopDevice";
227 result = result.arg( MARBLE_VERSION_STRING, device, platform, component);
228 return result.toLatin1();
229 }
230
231 #include "moc_HttpDownloadManager.cpp"
232