rssguard/src/librssguard/network-web/networkfactory.cpp
Martin Rotter e3e511dcba save
2021-01-29 20:25:42 +01:00

270 lines
8.9 KiB
C++

// For license of this file, see <project-root-folder>/LICENSE.md.
#include "network-web/networkfactory.h"
#include "definitions/definitions.h"
#include "miscellaneous/settings.h"
#include "network-web/downloader.h"
#include "network-web/silentnetworkaccessmanager.h"
#include <QEventLoop>
#include <QIcon>
#include <QPixmap>
#include <QRegularExpression>
#include <QTextDocument>
#include <QTimer>
QStringList NetworkFactory::extractFeedLinksFromHtmlPage(const QUrl& url, const QString& html) {
QStringList feeds;
QRegularExpression rx(FEED_REGEX_MATCHER, QRegularExpression::PatternOption::CaseInsensitiveOption);
QRegularExpression rx_href(FEED_HREF_REGEX_MATCHER, QRegularExpression::PatternOption::CaseInsensitiveOption);
rx_href.optimize();
QRegularExpressionMatchIterator it_rx = rx.globalMatch(html);
while (it_rx.hasNext()) {
QRegularExpressionMatch mat_tx = it_rx.next();
QString link_tag = mat_tx.captured();
QString feed_link = rx_href.match(link_tag).captured(1);
if (feed_link.startsWith(QL1S("//"))) {
feed_link = QString(URI_SCHEME_HTTP) + feed_link.mid(2);
}
else if (feed_link.startsWith(QL1C('/'))) {
feed_link = url.toString(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::StripTrailingSlash) + feed_link;
}
feeds.append(feed_link);
}
return feeds;
}
QPair<QByteArray, QByteArray> NetworkFactory::generateBasicAuthHeader(const QString& username, const QString& password) {
if (username.isEmpty()) {
return QPair<QByteArray, QByteArray>(QByteArray(), QByteArray());
}
else {
QString basic_value = username + ":" + password;
QString header_value = QString("Basic ") + QString(basic_value.toUtf8().toBase64());
return QPair<QByteArray, QByteArray>(HTTP_HEADERS_AUTHORIZATION, header_value.toLocal8Bit());
}
}
QString NetworkFactory::networkErrorText(QNetworkReply::NetworkError error_code) {
switch (error_code) {
case QNetworkReply::ProtocolUnknownError:
case QNetworkReply::ProtocolFailure:
//: Network status.
return tr("protocol error");
case QNetworkReply::ContentAccessDenied:
return tr("access to content was denied");
case QNetworkReply::HostNotFoundError:
//: Network status.
return tr("host not found");
case QNetworkReply::OperationCanceledError:
case QNetworkReply::TimeoutError:
return tr("connection timed out or was cancelled");
case QNetworkReply::RemoteHostClosedError:
case QNetworkReply::ConnectionRefusedError:
//: Network status.
return tr("connection refused");
case QNetworkReply::ProxyTimeoutError:
//: Network status.
return tr("connection timed out");
case QNetworkReply::SslHandshakeFailedError:
//: Network status.
return tr("SSL handshake failed");
case QNetworkReply::ProxyConnectionClosedError:
case QNetworkReply::ProxyConnectionRefusedError:
//: Network status.
return tr("proxy server connection refused");
case QNetworkReply::TemporaryNetworkFailureError:
//: Network status.
return tr("temporary failure");
case QNetworkReply::AuthenticationRequiredError:
//: Network status.
return tr("authentication failed");
case QNetworkReply::ProxyAuthenticationRequiredError:
//: Network status.
return tr("proxy authentication required");
case QNetworkReply::ProxyNotFoundError:
//: Network status.
return tr("proxy server not found");
case QNetworkReply::NoError:
//: Network status.
return tr("no errors");
case QNetworkReply::UnknownContentError:
//: Network status.
return tr("unknown content");
case QNetworkReply::ContentNotFoundError:
//: Network status.
return tr("content not found");
default:
//: Network status.
return tr("unknown error");
}
}
QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QList<QString>& urls, int timeout,
QIcon& output, const QNetworkProxy& custom_proxy) {
QNetworkReply::NetworkError network_result = QNetworkReply::NetworkError::UnknownNetworkError;
for (const QString& url : urls) {
QByteArray icon_data;
network_result = performNetworkOperation(url,
timeout,
QByteArray(),
icon_data,
QNetworkAccessManager::Operation::GetOperation,
{},
false,
{},
{},
custom_proxy).first;
if (network_result == QNetworkReply::NetworkError::NoError) {
QPixmap icon_pixmap;
icon_pixmap.loadFromData(icon_data);
output = QIcon(icon_pixmap);
if (!output.isNull()) {
break;
}
}
QString host = QUrl(url).host();
if (host.startsWith(QSL("www."))) {
host = host.mid(4);
}
const QString google_s2_with_url = QString("http://www.google.com/s2/favicons?domain=%1").arg(host);
network_result = performNetworkOperation(google_s2_with_url,
timeout,
QByteArray(),
icon_data,
QNetworkAccessManager::Operation::GetOperation,
{},
false,
{},
{},
custom_proxy).first;
if (network_result == QNetworkReply::NoError) {
QPixmap icon_pixmap;
icon_pixmap.loadFromData(icon_data);
output = QIcon(icon_pixmap);
if (!output.isNull()) {
break;
}
}
}
return network_result;
}
NetworkResult NetworkFactory::performNetworkOperation(const QString& url, int timeout, const QByteArray& input_data,
QByteArray& output, QNetworkAccessManager::Operation operation,
QList<QPair<QByteArray, QByteArray>> additional_headers,
bool protected_contents,
const QString& username, const QString& password,
const QNetworkProxy& custom_proxy) {
Downloader downloader;
QEventLoop loop;
NetworkResult result;
// We need to quit event loop when the download finishes.
QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit);
for (const auto& header : additional_headers) {
if (!header.first.isEmpty()) {
downloader.appendRawHeader(header.first, header.second);
}
}
if (custom_proxy.type() != QNetworkProxy::ProxyType::DefaultProxy) {
downloader.setProxy(custom_proxy);
}
downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password);
loop.exec();
output = downloader.lastOutputData();
result.first = downloader.lastOutputError();
result.second = downloader.lastContentType();
return result;
}
NetworkResult NetworkFactory::performNetworkOperation(const QString& url,
int timeout,
QHttpMultiPart* input_data,
QList<HttpResponse>& output,
QNetworkAccessManager::Operation operation,
QList<QPair<QByteArray, QByteArray>> additional_headers,
bool protected_contents,
const QString& username,
const QString& password,
const QNetworkProxy& custom_proxy) {
Downloader downloader;
QEventLoop loop;
NetworkResult result;
// We need to quit event loop when the download finishes.
QObject::connect(&downloader, &Downloader::completed, &loop, &QEventLoop::quit);
for (const auto& header : additional_headers) {
if (!header.first.isEmpty()) {
downloader.appendRawHeader(header.first, header.second);
}
}
if (custom_proxy.type() != QNetworkProxy::ProxyType::DefaultProxy) {
downloader.setProxy(custom_proxy);
}
downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password);
loop.exec();
output = downloader.lastOutputMultipartData();
result.first = downloader.lastOutputError();
result.second = downloader.lastContentType();
return result;
}