diff options
Diffstat (limited to 'utils/rbutilqt/base/httpget.cpp')
-rw-r--r-- | utils/rbutilqt/base/httpget.cpp | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/utils/rbutilqt/base/httpget.cpp b/utils/rbutilqt/base/httpget.cpp new file mode 100644 index 0000000000..0cd9236209 --- /dev/null +++ b/utils/rbutilqt/base/httpget.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2013 by Dominik Riebeling + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include <QtNetwork> + +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QSslConfiguration> + +#include "httpget.h" +#include "Logger.h" + +QString HttpGet::m_globalUserAgent; //< globally set user agent for requests +QDir HttpGet::m_globalCache; //< global cach path value for new objects +QNetworkProxy HttpGet::m_globalProxy; +QList<QSslCertificate> HttpGet::m_acceptedClientCerts; + +HttpGet::HttpGet(QObject *parent) + : QObject(parent), + m_mgr(this), + m_reply(nullptr), + m_cache(nullptr), + m_cachedir(m_globalCache), + m_outputFile(nullptr), + m_proxy(QNetworkProxy::NoProxy) +{ + setCache(true); + connect(&m_mgr, &QNetworkAccessManager::finished, this, + static_cast<void (HttpGet::*)(QNetworkReply*)>(&HttpGet::requestFinished)); + m_lastServerTimestamp = QDateTime(); +} + + +/** @brief set cache path + * @param d new directory to use as cache path + */ +void HttpGet::setCache(const QDir& d) +{ + if(m_cache && m_cachedir == d.absolutePath()) + return; + m_cachedir.setPath(d.absolutePath()); + setCache(true); +} + + +/** @brief enable / disable cache useage + * @param c set cache usage + */ +void HttpGet::setCache(bool c) +{ + // don't change cache if it's already (un)set. + if(c && m_cache) return; + if(!c && !m_cache) return; + // don't delete the old cache directly, it might still be in use. Just + // instruct it to delete itself later. + if(m_cache) m_cache->deleteLater(); + m_cache = nullptr; + + QString path = m_cachedir.absolutePath(); + + if(!c || m_cachedir.absolutePath().isEmpty()) { + LOG_INFO() << "disabling download cache"; + } + else { + // append the cache path to make it unique in case the path points to + // the system temporary path. In that case using it directly might + // cause problems. Extra path also used in configure dialog. + path += "/rbutil-cache"; + LOG_INFO() << "setting cache folder to" << path; + m_cache = new QNetworkDiskCache(this); + m_cache->setCacheDirectory(path); + } + m_mgr.setCache(m_cache); +} + + +/** @brief read all downloaded data into a buffer + * @return data + */ +QByteArray HttpGet::readAll() +{ + return m_data; +} + + +/** @brief Set and enable Proxy to use. + * @param proxy Proxy URL. + */ +void HttpGet::setProxy(const QUrl &proxy) +{ + LOG_INFO() << "Proxy set to" << proxy; + m_proxy.setType(QNetworkProxy::HttpProxy); + m_proxy.setHostName(proxy.host()); + m_proxy.setPort(proxy.port()); + m_proxy.setUser(proxy.userName()); + m_proxy.setPassword(proxy.password()); + m_mgr.setProxy(m_proxy); +} + + +/** @brief Enable or disable use of previously set proxy. + * @param enable Enable proxy. + */ +void HttpGet::setProxy(bool enable) +{ + if(enable) m_mgr.setProxy(m_proxy); + else m_mgr.setProxy(QNetworkProxy::NoProxy); +} + + +/** @brief Set output file. + * + * Set filename for storing the downloaded file to. If no file is set the + * downloaded file will not be stored to disk but kept in memory. The result + * can then be retrieved using readAll(). + * + * @param file Output file. + */ +void HttpGet::setFile(QFile *file) +{ + m_outputFile = file; +} + + +void HttpGet::abort() +{ + if(m_reply) m_reply->abort(); +} + + +void HttpGet::requestFinished(QNetworkReply* reply) +{ + m_lastStatusCode + = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + LOG_INFO() << "Request finished, status code:" << m_lastStatusCode << reply->error(); + m_lastServerTimestamp + = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().toLocalTime(); + LOG_INFO() << "Data from cache:" + << reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); + m_lastRequestCached = + reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); + if(reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isValid()) { + // handle relative URLs using QUrl::resolved() + QUrl org = reply->request().url(); + QUrl url = QUrl(org).resolved( + reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl()); + // reconstruct query +#if QT_VERSION < 0x050000 + QList<QPair<QByteArray, QByteArray> > qitms = org.encodedQueryItems(); + for(int i = 0; i < qitms.size(); ++i) + url.addEncodedQueryItem(qitms.at(i).first, qitms.at(i).second); +#else + url.setQuery(org.query()); +#endif + LOG_INFO() << "Redirected to" << url; + startRequest(url); + return; + } + else if(m_lastStatusCode == 200 || + (reply->url().scheme() == "file" && reply->error() == 0)) { + // callers might not be aware if the request is file:// so fake 200. + m_lastStatusCode = 200; + m_data = reply->readAll(); + if(m_outputFile && m_outputFile->open(QIODevice::WriteOnly)) { + m_outputFile->write(m_data); + m_outputFile->close(); + } + emit done(QNetworkReply::NoError); + } + else { + m_data.clear(); + emit done(reply->error()); + } + reply->deleteLater(); + m_reply = nullptr; +} + + +void HttpGet::downloadProgress(qint64 received, qint64 total) +{ + emit dataReadProgress((int)received, (int)total); +} + + +void HttpGet::startRequest(QUrl url) +{ + LOG_INFO() << "Request started"; + QNetworkRequest req(url); + if(!m_globalUserAgent.isEmpty()) + req.setRawHeader("User-Agent", m_globalUserAgent.toLatin1()); + + m_reply = m_mgr.get(req); +#if QT_VERSION < 0x050f00 + connect(m_reply, + static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error), + this, &HttpGet::networkError); +#else + connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpGet::networkError); +#endif + connect(m_reply, &QNetworkReply::downloadProgress, this, &HttpGet::downloadProgress); + connect(m_reply, &QNetworkReply::sslErrors, this, &HttpGet::gotSslError); +} + + +void HttpGet::gotSslError(const QList<QSslError> &errors) +{ + LOG_WARNING() << "Got SSL error" << errors; + + // if this is a cert error, and only if we already accepted a remote cert + // ignore the error. + // This will make QNAM continue the request and finish it. + if (errors.size() == 1 + && errors.at(0).error() == QSslError::UnableToGetLocalIssuerCertificate + && m_acceptedClientCerts.contains(m_reply->sslConfiguration().peerCertificate())) { + LOG_INFO() << "client cert temporarily trusted by user."; + m_reply->ignoreSslErrors(); + } + else { + LOG_ERROR() << m_reply->sslConfiguration().peerCertificate().toText(); + emit sslError(errors.at(0), m_reply->sslConfiguration().peerCertificate()); + } + +} + +void HttpGet::networkError(QNetworkReply::NetworkError error) +{ + LOG_ERROR() << "NetworkError occured:" << error << m_reply->errorString(); + m_lastErrorString = m_reply->errorString(); +} + + +/** @brief Retrieve the file pointed to by url. + * + * Note: This also handles file:// URLs. Be aware that QUrl requires file:// + * URLs to be absolute, i.e. file://filename.txt doesn't work. Use + * QDir::absoluteFilePath() to convert to an absolute path first. + * + * @param url URL to download. + */ +void HttpGet::getFile(const QUrl &url) +{ + LOG_INFO() << "Get URI" << url.toString(); + m_data.clear(); + startRequest(url); +} + + +/** @brief Retrieve string representation for most recent error. + * @return Error string. + */ +QString HttpGet::errorString(void) +{ + return m_lastErrorString; +} + + +/** @brief Return last HTTP response code. + * @return Response code. + */ +int HttpGet::httpResponse(void) +{ + return m_lastStatusCode; +} + |