Show webfinger if displayName is unknown for an author.
[larjonas-pumpa.git] / src / filedownloader.cpp
blob6ec61d7df6ad07b787d6274485ee5ee92be2dac4
1 /*
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"
23 #include "util.h"
25 #ifdef QT5
26 #include <QStandardPaths>
27 #else
28 #include <QDesktopServices>
29 #endif
31 #include <QCryptographicHash>
33 //------------------------------------------------------------------------------
35 FileDownloadManager* FileDownloadManager::s_instance = NULL;
37 //------------------------------------------------------------------------------
39 FileDownloadManager::FileDownloadManager(QObject* parent) : QObject(parent) {
40 m_nextRequestId = 0;
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);
59 return s_instance;
62 //------------------------------------------------------------------------------
64 void FileDownloadManager::dumpStats() {
65 // QMap<QString, FileDownloader*> m_inProgress;
66 QMapIterator<QString, FileDownloader*> it(m_inProgress);
67 while (it.hasNext()) {
68 it.next();
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()) {
76 itt.next();
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);
99 QPixmap pix(fn);
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
103 // formats.
105 if (pix.isNull())
106 pix.load(fn, "JPEG");
108 if (pix.isNull())
109 pix.load(fn, "PNG");
111 if (pix.isNull())
112 pix.load(fn, "GIF");
114 if (pix.isNull() && !brokenImage.isEmpty())
115 pix.load(brokenImage);
117 return pix;
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);
128 return mov;
131 //------------------------------------------------------------------------------
133 bool FileDownloadManager::supportsAnimation(QString url) {
134 QString fn = urlToPath(url);
136 QImageReader r(fn);
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()) {
152 m_cacheDir =
153 #ifdef QT5
154 QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
155 #else
156 QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
157 #endif
158 if (m_cacheDir.isEmpty())
159 m_cacheDir = slashify(QDir::homePath())+".cache/";
160 else
161 m_cacheDir = slashify(m_cacheDir);
162 m_cacheDir += "pumpa/";
164 QString path = m_cacheDir;
165 QDir d;
166 d.mkpath(path);
168 QString ending;
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())
173 ending = ".png";
175 hash.reset();
176 hash.addData(url.toUtf8());
178 QString hashStr = hash.result().toHex();
180 QString ret = path + hashStr + ending;
181 m_urlMap.insert(url, ret);
182 return ret;
185 //------------------------------------------------------------------------------
187 FileDownloader* FileDownloadManager::download(QString url) {
188 if (m_inProgress.contains(url))
189 return m_inProgress[url];
191 #ifdef DEBUG_NET
192 qDebug() << "[DOWNLOAD]" << url;
193 #endif
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)));
200 return fd;
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
210 m_nextRequestId = 0;
211 while (m_requestMap.contains(m_nextRequestId))
212 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
224 #ifdef DEBUG_NET
225 qDebug() << "FileDownloadManager SSL ERROR" << nr->url();
226 #endif
229 //------------------------------------------------------------------------------
231 void FileDownloadManager::onAuthorizedRequestReady(QByteArray response,
232 int id) {
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());
245 if (!fd)
246 return;
248 m_inProgress.remove(fd->url());
249 fd->deleteLater();
252 //------------------------------------------------------------------------------
254 FileDownloader::FileDownloader(QString url, FileDownloadManager* fdm) :
255 QObject(fdm),
256 m_url(url),
257 m_oar(NULL),
258 m_redirs(0),
259 m_fdm(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);
276 } else {
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();
290 if (nr->error()) {
291 emit networkError(tr("Network error: ")+nr->errorString());
292 nr->deleteLater();
293 return;
296 if (status >= 301 && status <= 399) {
297 if (m_redirs > 5) {
298 emit networkError(tr("Network error: too many redirections!"));
299 nr->deleteLater();
300 return;
303 QUrl newUrl = nr->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
304 m_redirs++;
306 QNetworkReply* nr2 = m_fdm->m_nam->get(QNetworkRequest(nr->url().resolved(newUrl)));
307 connect(nr2, SIGNAL(finished()), this, SLOT(replyFinished()));
308 nr->deleteLater();
309 return;
312 requestReady(nr->readAll(), NULL);
313 nr->deleteLater();
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)."))
321 .arg(m_url)
322 .arg(m_fdm->m_oam->lastError()));
323 return;
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());
332 delete fp;
333 return;
335 fp->write(response);
336 fp->close();
337 delete fp;
339 QPixmap pix = m_fdm->pixmap(m_url);
341 resizeImage(pix, fn);
343 emit fileReady();
346 //------------------------------------------------------------------------------
348 void FileDownloader::resizeImage(QPixmap pix, QString fn) {
349 if (pix.isNull())
350 return;
352 int w = pix.width();
353 int h = pix.height();
355 if (w <= IMAGE_MAX_WIDTH && h <= IMAGE_MAX_HEIGHT)
356 return;
358 QPixmap newPix;
359 if (w > h)
360 newPix = pix.scaledToWidth(IMAGE_MAX_WIDTH);
361 else
362 newPix = pix.scaledToHeight(IMAGE_MAX_HEIGHT);
363 newPix.save(fn);