summaryrefslogtreecommitdiffstats
path: root/utils/rbutilqt/base/httpget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/rbutilqt/base/httpget.cpp')
-rw-r--r--utils/rbutilqt/base/httpget.cpp279
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;
+}
+