2 Copyright 2013-2015 Mats Sjöberg
4 This file is part of the Pumpa programme.
6 Pumpa is free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 Pumpa is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pumpa. If not, see <http://www.gnu.org/licenses/>.
20 #include "filedownloader.h"
21 #include "pumpa_defines.h"
22 #include "pumpasettings.h"
26 #include <QStandardPaths>
28 #include <QDesktopServices>
31 #include <QCryptographicHash>
33 //------------------------------------------------------------------------------
35 FileDownloadManager
* FileDownloadManager::s_instance
= NULL
;
37 //------------------------------------------------------------------------------
39 FileDownloadManager::FileDownloadManager(QObject
* parent
) : QObject(parent
) {
42 m_nam
= new QNetworkAccessManager(this);
43 // connect(m_nam, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)),
44 // this, SLOT(onSslErrors(QNetworkReply*, QList<QSslError>)));
46 m_oam
= new KQOAuthManager(this);
47 connect(m_oam
, SIGNAL(authorizedRequestReady(QByteArray
, int)),
48 this, SLOT(onAuthorizedRequestReady(QByteArray
, int)));
49 connect(m_oam
, SIGNAL(sslErrors(QNetworkReply
*, QList
<QSslError
>)),
50 this, SLOT(onSslErrors(QNetworkReply
*, QList
<QSslError
>)));
53 //------------------------------------------------------------------------------
55 FileDownloadManager
* FileDownloadManager::getManager(QObject
* parent
) {
56 if (s_instance
== NULL
&& parent
!= 0)
57 s_instance
= new FileDownloadManager(parent
);
62 //------------------------------------------------------------------------------
64 void FileDownloadManager::dumpStats() {
65 // QMap<QString, FileDownloader*> m_inProgress;
66 QMapIterator
<QString
, FileDownloader
*> it(m_inProgress
);
67 while (it
.hasNext()) {
69 qDebug() << "[FILEDOWNLOADMANAGER] in progress" << it
.key();
72 // QMap<QString, QString> m_urlMap;
73 // QMap<int, requestData_t> m_requestMap;
74 QMapIterator
<int, requestData_t
> itt(m_requestMap
);
75 while (itt
.hasNext()) {
77 qDebug() << "[FILEDOWNLOADMANAGER] requests" << itt
.key();
81 //------------------------------------------------------------------------------
83 bool FileDownloadManager::hasFile(QString url
) {
84 return !fileName(url
).isEmpty();
87 //------------------------------------------------------------------------------
89 QString
FileDownloadManager::fileName(QString url
) {
90 QString fn
= urlToPath(url
);
91 return QFile::exists(fn
) ? fn
: "";
94 //------------------------------------------------------------------------------
96 QPixmap
FileDownloadManager::pixmap(QString url
, QString brokenImage
) {
97 QString fn
= urlToPath(url
);
101 // Sometimes files are given with the wrong file ending, or Qt is
102 // unable to guess the format so we try to force them into popular
106 pix
.load(fn
, "JPEG");
114 if (pix
.isNull() && !brokenImage
.isEmpty())
115 pix
.load(brokenImage
);
120 //------------------------------------------------------------------------------
122 QMovie
* FileDownloadManager::movie(QString url
) {
123 QString fn
= urlToPath(url
);
125 QMovie
* mov
= new QMovie(fn
, QByteArray(), this);
126 mov
->setCacheMode(QMovie::CacheAll
);
131 //------------------------------------------------------------------------------
133 bool FileDownloadManager::supportsAnimation(QString url
) {
134 QString fn
= urlToPath(url
);
137 return r
.supportsAnimation();
140 //------------------------------------------------------------------------------
142 QString
FileDownloadManager::urlToPath(QString url
) {
143 if (m_urlMap
.contains(url
))
144 return m_urlMap
[url
];
146 static QCryptographicHash
hash(QCryptographicHash::Md5
);
147 static QStringList knownEndings
;
148 if (knownEndings
.isEmpty())
149 knownEndings
<< ".png" << ".jpeg" << ".jpg" << ".gif";
151 if (m_cacheDir
.isEmpty()) {
154 QStandardPaths::writableLocation(QStandardPaths::CacheLocation
);
156 QDesktopServices::storageLocation(QDesktopServices::CacheLocation
);
158 if (m_cacheDir
.isEmpty())
159 m_cacheDir
= slashify(QDir::homePath())+".cache/";
161 m_cacheDir
= slashify(m_cacheDir
);
162 m_cacheDir
+= "pumpa/";
164 QString path
= m_cacheDir
;
169 for (int i
=0; i
<knownEndings
.count() && ending
.isEmpty(); i
++)
170 if (url
.endsWith(knownEndings
[i
]))
171 ending
= knownEndings
[i
];
172 if (ending
.isEmpty())
176 hash
.addData(url
.toUtf8());
178 QString hashStr
= hash
.result().toHex();
180 QString ret
= path
+ hashStr
+ ending
;
181 m_urlMap
.insert(url
, ret
);
185 //------------------------------------------------------------------------------
187 FileDownloader
* FileDownloadManager::download(QString url
) {
188 if (m_inProgress
.contains(url
))
189 return m_inProgress
[url
];
192 qDebug() << "[DOWNLOAD]" << url
;
195 FileDownloader
* fd
= new FileDownloader(url
, this);
196 m_inProgress
.insert(url
, fd
);
197 connect(fd
, SIGNAL(fileReady()), this, SLOT(onFileReady()));
198 connect(fd
, SIGNAL(networkError(QString
)), this, SLOT(onFileReady(QString
)));
203 //------------------------------------------------------------------------------
205 void FileDownloadManager::executeAuthorizedRequest(KQOAuthRequest
* oar
,
206 FileDownloader
* fd
) {
207 int id
= m_nextRequestId
++;
209 if (m_nextRequestId
> 32000) { // bound to be smaller than any MAX_INT
211 while (m_requestMap
.contains(m_nextRequestId
))
215 m_requestMap
.insert(id
, qMakePair(oar
, fd
));
216 m_oam
->executeAuthorizedRequest(oar
, id
);
220 //------------------------------------------------------------------------------
222 void FileDownloadManager::onSslErrors(QNetworkReply
* nr
, QList
<QSslError
>) {
223 (void) nr
; // suppress unused warning
225 qDebug() << "FileDownloadManager SSL ERROR" << nr
->url();
229 //------------------------------------------------------------------------------
231 void FileDownloadManager::onAuthorizedRequestReady(QByteArray response
,
233 QPair
<KQOAuthRequest
*, FileDownloader
*> rp
= m_requestMap
.take(id
);
234 KQOAuthRequest
* oar
= rp
.first
;
235 FileDownloader
* fd
= rp
.second
;
237 fd
->requestReady(response
, oar
);
240 //------------------------------------------------------------------------------
242 void FileDownloadManager::onFileReady(QString
) {
243 FileDownloader
*fd
= qobject_cast
<FileDownloader
*>(sender());
248 m_inProgress
.remove(fd
->url());
252 //------------------------------------------------------------------------------
254 FileDownloader::FileDownloader(QString url
, FileDownloadManager
* fdm
) :
261 PumpaSettings
* ps
= PumpaSettings::getSettings();
263 if (ps
&& m_url
.startsWith(ps
->siteUrl())) {
264 m_oar
= new KQOAuthRequest(this);
265 m_oar
->initRequest(KQOAuthRequest::AuthorizedRequest
, QUrl(m_url
));
267 m_oar
->setConsumerKey(ps
->clientId());
268 m_oar
->setConsumerSecretKey(ps
->clientSecret());
269 m_oar
->setToken(ps
->token());
270 m_oar
->setTokenSecret(ps
->tokenSecret());
272 m_oar
->setHttpMethod(KQOAuthRequest::GET
);
273 m_oar
->setTimeout(60000); // one minute time-out
275 m_fdm
->executeAuthorizedRequest(m_oar
, this);
277 QNetworkReply
* nr
= m_fdm
->m_nam
->get(QNetworkRequest(QUrl(m_url
)));
278 connect(nr
, SIGNAL(finished()), this, SLOT(replyFinished()));
282 //------------------------------------------------------------------------------
284 void FileDownloader::replyFinished() {
285 QNetworkReply
*nr
= qobject_cast
<QNetworkReply
*>(sender());
286 QString url
= nr
->url().toString();
288 int status
= nr
->attribute(QNetworkRequest::HttpStatusCodeAttribute
).toInt();
291 emit
networkError(tr("Network error: ")+nr
->errorString());
296 if (status
>= 301 && status
<= 399) {
298 emit
networkError(tr("Network error: too many redirections!"));
303 QUrl newUrl
= nr
->attribute(QNetworkRequest::RedirectionTargetAttribute
).toUrl();
306 QNetworkReply
* nr2
= m_fdm
->m_nam
->get(QNetworkRequest(nr
->url().resolved(newUrl
)));
307 connect(nr2
, SIGNAL(finished()), this, SLOT(replyFinished()));
312 requestReady(nr
->readAll(), NULL
);
316 //------------------------------------------------------------------------------
318 void FileDownloader::requestReady(QByteArray response
, KQOAuthRequest
* oar
) {
319 if (oar
!= NULL
&& m_fdm
->m_oam
->lastError()) {
320 emit
networkError(QString(tr("Unable to download %1 (Error #%2)."))
322 .arg(m_fdm
->m_oam
->lastError()));
326 QString fn
= m_fdm
->urlToPath(m_url
);
328 QFile
* fp
= new QFile(fn
);
329 if (!fp
->open(QIODevice::WriteOnly
)) {
330 emit
networkError(QString(tr("Could not open file %1 for writing: ")).
331 arg(fn
) + fp
->errorString());
339 QPixmap pix
= m_fdm
->pixmap(m_url
);
341 resizeImage(pix
, fn
);
346 //------------------------------------------------------------------------------
348 void FileDownloader::resizeImage(QPixmap pix
, QString fn
) {
353 int h
= pix
.height();
355 if (w
<= IMAGE_MAX_WIDTH
&& h
<= IMAGE_MAX_HEIGHT
)
360 newPix
= pix
.scaledToWidth(IMAGE_MAX_WIDTH
);
362 newPix
= pix
.scaledToHeight(IMAGE_MAX_HEIGHT
);