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