rssguard/src/librssguard/services/tt-rss/ttrssnetworkfactory.cpp
2021-09-24 10:40:31 +02:00

904 lines
38 KiB
C++

// For license of this file, see <project-root-folder>/LICENSE.md.
#include "services/tt-rss/ttrssnetworkfactory.h"
#include "3rd-party/boolinq/boolinq.h"
#include "definitions/definitions.h"
#include "miscellaneous/application.h"
#include "miscellaneous/iconfactory.h"
#include "miscellaneous/textfactory.h"
#include "network-web/networkfactory.h"
#include "services/abstract/category.h"
#include "services/abstract/label.h"
#include "services/abstract/labelsnode.h"
#include "services/abstract/rootitem.h"
#include "services/abstract/serviceroot.h"
#include "services/tt-rss/definitions.h"
#include "services/tt-rss/ttrssfeed.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QPair>
#include <QVariant>
TtRssNetworkFactory::TtRssNetworkFactory()
: m_bareUrl(QString()), m_fullUrl(QString()), m_username(QString()), m_password(QString()), m_batchSize(TTRSS_DEFAULT_MESSAGES),
m_forceServerSideUpdate(false), m_authIsUsed(false), m_authUsername(QString()), m_authPassword(QString()), m_sessionId(QString()),
m_lastError(QNetworkReply::NoError) {}
QString TtRssNetworkFactory::url() const {
return m_bareUrl;
}
void TtRssNetworkFactory::setUrl(const QString& url) {
m_bareUrl = url;
if (!m_bareUrl.endsWith(QSL("/"))) {
m_bareUrl = m_bareUrl + QSL("/");
}
if (!m_bareUrl.endsWith(QSL("api/"))) {
m_fullUrl = m_bareUrl + QSL("api/");
}
else {
m_fullUrl = m_bareUrl;
}
}
QString TtRssNetworkFactory::username() const {
return m_username;
}
void TtRssNetworkFactory::setUsername(const QString& username) {
m_username = username;
}
QString TtRssNetworkFactory::password() const {
return m_password;
}
void TtRssNetworkFactory::setPassword(const QString& password) {
m_password = password;
}
QDateTime TtRssNetworkFactory::lastLoginTime() const {
return m_lastLoginTime;
}
QNetworkReply::NetworkError TtRssNetworkFactory::lastError() const {
return m_lastError;
}
TtRssLoginResponse TtRssNetworkFactory::login(const QNetworkProxy& proxy) {
if (!m_sessionId.isEmpty()) {
qWarningNN << LOGSEC_TTRSS
<< "Session ID is not empty before login, logging out first.";
logout(proxy);
}
QJsonObject json;
json[QSL("op")] = QSL("login");
json[QSL("user")] = m_username;
json[QSL("password")] = m_password;
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
qApp->settings()->value(GROUP(Feeds),
SETTING(
Feeds::UpdateTimeout)).toInt(),
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssLoginResponse login_response(QString::fromUtf8(result_raw));
if (network_reply.first == QNetworkReply::NoError) {
m_sessionId = login_response.sessionId();
m_lastLoginTime = QDateTime::currentDateTime();
}
else {
qWarningNN << LOGSEC_TTRSS
<< "Login failed with error:"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return login_response;
}
TtRssResponse TtRssNetworkFactory::logout(const QNetworkProxy& proxy) {
if (!m_sessionId.isEmpty()) {
QJsonObject json;
json[QSL("op")] = QSL("logout");
json[QSL("sid")] = m_sessionId;
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
qApp->settings()->value(GROUP(Feeds),
SETTING(
Feeds::UpdateTimeout)).toInt(),
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
m_lastError = network_reply.first;
if (m_lastError == QNetworkReply::NoError) {
m_sessionId.clear();
}
else {
qWarningNN << LOGSEC_TTRSS
<< "Logout failed with error:"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
return TtRssResponse(QString::fromUtf8(result_raw));
}
else {
qWarningNN << LOGSEC_TTRSS
<< "Cannot logout because session ID is empty.";
m_lastError = QNetworkReply::NetworkError::NoError;
return TtRssResponse();
}
}
TtRssGetLabelsResponse TtRssNetworkFactory::getLabels(const QNetworkProxy& proxy) {
QJsonObject json;
json[QSL("op")] = QSL("getLabels");
json[QSL("sid")] = m_sessionId;
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers);
TtRssGetLabelsResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssGetLabelsResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "getLabels failed with error:"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories(const QNetworkProxy& proxy) {
QJsonObject json;
json[QSL("op")] = QSL("getFeedTree");
json[QSL("sid")] = m_sessionId;
json[QSL("include_empty")] = true;
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl, timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "getFeedTree failed with error:"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssGetHeadlinesResponse TtRssNetworkFactory::getHeadlines(int feed_id, int limit, int skip,
bool show_content, bool include_attachments,
bool sanitize, bool unread_only,
const QNetworkProxy& proxy) {
QJsonObject json;
json[QSL("op")] = QSL("getHeadlines");
json[QSL("sid")] = m_sessionId;
json[QSL("feed_id")] = feed_id;
json[QSL("force_update")] = m_forceServerSideUpdate;
json[QSL("limit")] = limit;
json[QSL("skip")] = skip;
json[QSL("view_mode")] = unread_only ? QSL("unread") : QSL("all_articles");
json[QSL("show_content")] = show_content;
json[QSL("include_attachments")] = include_attachments;
json[QSL("sanitize")] = sanitize;
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "getHeadlines failed with error:"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssResponse TtRssNetworkFactory::setArticleLabel(const QStringList& article_ids, const QString& label_custom_id,
bool assign, const QNetworkProxy& proxy) {
QJsonObject json;
json[QSL("op")] = QSL("setArticleLabel");
json[QSL("sid")] = m_sessionId;
json[QSL("article_ids")] = article_ids.join(QSL(","));
json[QSL("label_id")] = label_custom_id.toInt();
json[QSL("assign")] = assign;
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "setArticleLabel failed with error"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList& ids,
UpdateArticle::OperatingField field,
UpdateArticle::Mode mode,
const QNetworkProxy& proxy) {
QJsonObject json;
json[QSL("op")] = QSL("updateArticle");
json[QSL("sid")] = m_sessionId;
json[QSL("article_ids")] = ids.join(QSL(","));
json[QSL("mode")] = int(mode);
json[QSL("field")] = int(field);
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssUpdateArticleResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssUpdateArticleResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "updateArticle failed with error"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssSubscribeToFeedResponse TtRssNetworkFactory::subscribeToFeed(const QString& url, int category_id,
const QNetworkProxy& proxy,
bool protectd, const QString& username,
const QString& password) {
QJsonObject json;
json[QSL("op")] = QSL("subscribeToFeed");
json[QSL("sid")] = m_sessionId;
json[QSL("feed_url")] = url;
json[QSL("category_id")] = category_id;
if (protectd) {
json[QSL("login")] = username;
json[QSL("password")] = password;
}
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssSubscribeToFeedResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssSubscribeToFeedResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "updateArticle failed with error"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssUnsubscribeFeedResponse TtRssNetworkFactory::unsubscribeFeed(int feed_id, const QNetworkProxy& proxy) {
QJsonObject json;
json[QSL("op")] = QSL("unsubscribeFeed");
json[QSL("sid")] = m_sessionId;
json[QSL("feed_id")] = feed_id;
const int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
QByteArray result_raw;
QList<QPair<QByteArray, QByteArray>> headers;
headers << QPair<QByteArray, QByteArray>(HTTP_HEADERS_CONTENT_TYPE, TTRSS_CONTENT_TYPE_JSON);
headers << NetworkFactory::generateBasicAuthHeader(m_authUsername, m_authPassword);
NetworkResult network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
TtRssUnsubscribeFeedResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login(proxy);
json[QSL("sid")] = m_sessionId;
network_reply = NetworkFactory::performNetworkOperation(m_fullUrl,
timeout,
QJsonDocument(json).toJson(QJsonDocument::JsonFormat::Compact),
result_raw,
QNetworkAccessManager::Operation::PostOperation,
headers,
false,
{},
{},
proxy);
result = TtRssUnsubscribeFeedResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarningNN << LOGSEC_TTRSS
<< "getFeeds failed with error"
<< QUOTE_W_SPACE_DOT(network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
int TtRssNetworkFactory::batchSize() const {
return m_batchSize;
}
void TtRssNetworkFactory::setBatchSize(int batch_size) {
m_batchSize = batch_size;
}
bool TtRssNetworkFactory::downloadOnlyUnreadMessages() const {
return m_downloadOnlyUnreadMessages;
}
void TtRssNetworkFactory::setDownloadOnlyUnreadMessages(bool download_only_unread_messages) {
m_downloadOnlyUnreadMessages = download_only_unread_messages;
}
bool TtRssNetworkFactory::forceServerSideUpdate() const {
return m_forceServerSideUpdate;
}
void TtRssNetworkFactory::setForceServerSideUpdate(bool force_server_side_update) {
m_forceServerSideUpdate = force_server_side_update;
}
bool TtRssNetworkFactory::authIsUsed() const {
return m_authIsUsed;
}
void TtRssNetworkFactory::setAuthIsUsed(bool auth_is_used) {
m_authIsUsed = auth_is_used;
}
QString TtRssNetworkFactory::authUsername() const {
return m_authUsername;
}
void TtRssNetworkFactory::setAuthUsername(const QString& auth_username) {
m_authUsername = auth_username;
}
QString TtRssNetworkFactory::authPassword() const {
return m_authPassword;
}
void TtRssNetworkFactory::setAuthPassword(const QString& auth_password) {
m_authPassword = auth_password;
}
TtRssResponse::TtRssResponse(const QString& raw_content) {
m_rawContent = QJsonDocument::fromJson(raw_content.toUtf8()).object();
}
TtRssResponse::~TtRssResponse() = default;
bool TtRssResponse::isLoaded() const {
return !m_rawContent.isEmpty();
}
int TtRssResponse::seq() const {
if (!isLoaded()) {
return TTRSS_CONTENT_NOT_LOADED;
}
else {
return m_rawContent[QSL("seq")].toInt();
}
}
int TtRssResponse::status() const {
if (!isLoaded()) {
return TTRSS_CONTENT_NOT_LOADED;
}
else {
return m_rawContent[QSL("status")].toInt();
}
}
bool TtRssResponse::isNotLoggedIn() const {
return status() == TTRSS_API_STATUS_ERR && hasError() && error() == QSL(TTRSS_NOT_LOGGED_IN);
}
QString TtRssResponse::toString() const {
return QJsonDocument(m_rawContent).toJson(QJsonDocument::JsonFormat::Compact);
}
TtRssLoginResponse::TtRssLoginResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
TtRssLoginResponse::~TtRssLoginResponse() = default;
int TtRssLoginResponse::apiLevel() const {
if (!isLoaded()) {
return TTRSS_CONTENT_NOT_LOADED;
}
else {
return m_rawContent[QSL("content")].toObject()[QSL("api_level")].toInt();
}
}
QString TtRssLoginResponse::sessionId() const {
if (!isLoaded()) {
return QString();
}
else {
return m_rawContent[QSL("content")].toObject()[QSL("session_id")].toString();
}
}
QString TtRssResponse::error() const {
if (!isLoaded()) {
return QString();
}
else {
return m_rawContent[QSL("content")].toObject()[QSL("error")].toString();
}
}
bool TtRssResponse::hasError() const {
if (!isLoaded()) {
return false;
}
else {
return m_rawContent[QSL("content")].toObject().contains(QSL("error"));
}
}
TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() = default;
RootItem* TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, const QNetworkProxy& proxy, const QString& base_address) const {
auto* parent = new RootItem();
// Chop the "api/" from the end of the address.
qDebugNN << LOGSEC_TTRSS
<< "Base address to get feed icons is"
<< QUOTE_W_SPACE_DOT(base_address);
if (status() == TTRSS_API_STATUS_OK) {
// We have data, construct object tree according to data.
QJsonArray items_to_process = m_rawContent[QSL("content")].toObject()[QSL("categories")].toObject()[QSL("items")].toArray();
QVector<QPair<RootItem*, QJsonValue>> pairs; pairs.reserve(items_to_process.size());
for (const QJsonValue& item : items_to_process) {
pairs.append(QPair<RootItem*, QJsonValue>(parent, item));
}
while (!pairs.isEmpty()) {
QPair<RootItem*, QJsonValue> pair = pairs.takeFirst();
RootItem* act_parent = pair.first;
QJsonObject item = pair.second.toObject();
int item_id = item[QSL("bare_id")].toInt();
bool is_category = item.contains(QSL("type")) && item[QSL("type")].toString() == QSL(TTRSS_GFT_TYPE_CATEGORY);
if (item_id >= 0) {
if (is_category) {
if (item_id == 0) {
// This is "Uncategorized" category, all its feeds belong to top-level root.
if (item.contains(QSL("items"))) {
auto ite = item[QSL("items")].toArray();
for (const QJsonValue& child_feed : qAsConst(ite)) {
pairs.append(QPair<RootItem*, QJsonValue>(parent, child_feed));
}
}
}
else {
auto* category = new Category();
category->setTitle(item[QSL("name")].toString());
category->setCustomId(QString::number(item_id));
act_parent->appendChild(category);
if (item.contains(QSL("items"))) {
auto ite = item[QSL("items")].toArray();
for (const QJsonValue& child : qAsConst(ite)) {
pairs.append(QPair<RootItem*, QJsonValue>(category, child));
}
}
}
}
else {
// We have feed.
auto* feed = new TtRssFeed();
if (obtain_icons) {
QString icon_path = item[QSL("icon")].type() == QJsonValue::String ? item[QSL("icon")].toString() : QString();
if (!icon_path.isEmpty()) {
// Chop the "api/" suffix out and append
QString full_icon_address = base_address + QL1C('/') + icon_path;
QIcon icon;
auto res = NetworkFactory::downloadIcon({ { full_icon_address, true } },
DOWNLOAD_TIMEOUT,
icon,
proxy);
if (res == QNetworkReply::NoError) {
feed->setIcon(icon);
}
else {
qWarningNN << LOGSEC_TTRSS
<< "Failed to download icon with error"
<< QUOTE_W_SPACE_DOT(res);
}
}
}
feed->setTitle(item[QSL("name")].toString());
feed->setCustomId(QString::number(item_id));
act_parent->appendChild(feed);
}
}
}
}
return parent;
}
TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() = default;
QList<Message> TtRssGetHeadlinesResponse::messages(ServiceRoot* root) const {
QList<Message> messages;
auto active_labels = root->labelsNode() != nullptr ? root->labelsNode()->labels() : QList<Label*>();
auto json_msgs = m_rawContent[QSL("content")].toArray();
for (const QJsonValue& item : qAsConst(json_msgs)) {
QJsonObject mapped = item.toObject();
Message message;
message.m_author = mapped[QSL("author")].toString();
message.m_isRead = !mapped[QSL("unread")].toBool();
message.m_isImportant = mapped[QSL("marked")].toBool();
message.m_contents = mapped[QSL("content")].toString();
message.m_rawContents = QJsonDocument(mapped).toJson(QJsonDocument::JsonFormat::Compact);
auto json_labels = mapped[QSL("labels")].toArray();
for (const QJsonValue& lbl_val : qAsConst(json_labels)) {
QString lbl_custom_id = QString::number(lbl_val.toArray().at(0).toInt());
Label* label = boolinq::from(active_labels.begin(), active_labels.end()).firstOrDefault([lbl_custom_id](Label* lbl) {
return lbl->customId() == lbl_custom_id;
});
if (label != nullptr) {
message.m_assignedLabels.append(label);
}
else {
qWarningNN << LOGSEC_TTRSS << "Label with custom ID" << QUOTE_W_SPACE(lbl_custom_id)
<< "was not found. Maybe you need to perform sync-in to download it from server.";
}
}
// Multiply by 1000 because Tiny Tiny RSS API does not include miliseconds in Unix
// date/time number.
const qint64 t = static_cast<qint64>(mapped[QSL("updated")].toDouble()) * 1000;
message.m_created = TextFactory::parseDateTime(t);
message.m_createdFromFeed = true;
message.m_customId = QString::number(mapped[QSL("id")].toInt());
message.m_feedId = mapped[QSL("feed_id")].toString();
message.m_title = mapped[QSL("title")].toString();
message.m_url = mapped[QSL("link")].toString();
if (mapped.contains(QSL("attachments"))) {
// Process enclosures.
auto json_att = mapped[QSL("attachments")].toArray();
for (const QJsonValue& attachment : qAsConst(json_att)) {
QJsonObject mapped_attachemnt = attachment.toObject();
Enclosure enclosure;
enclosure.m_mimeType = mapped_attachemnt[QSL("content_type")].toString();
enclosure.m_url = mapped_attachemnt[QSL("content_url")].toString();
message.m_enclosures.append(enclosure);
}
}
messages.append(message);
}
return messages;
}
TtRssUpdateArticleResponse::TtRssUpdateArticleResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
TtRssUpdateArticleResponse::~TtRssUpdateArticleResponse() = default;
QString TtRssUpdateArticleResponse::updateStatus() const {
if (m_rawContent.contains(QSL("content"))) {
return m_rawContent[QSL("content")].toObject()[QSL("status")].toString();
}
else {
return QString();
}
}
int TtRssUpdateArticleResponse::articlesUpdated() const {
if (m_rawContent.contains(QSL("content"))) {
return m_rawContent[QSL("content")].toObject()[QSL("updated")].toInt();
}
else {
return 0;
}
}
TtRssSubscribeToFeedResponse::TtRssSubscribeToFeedResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
TtRssSubscribeToFeedResponse::~TtRssSubscribeToFeedResponse() = default;
int TtRssSubscribeToFeedResponse::code() const {
if (m_rawContent.contains(QSL("content"))) {
return m_rawContent[QSL("content")].toObject()[QSL("status")].toObject()[QSL("code")].toInt();
}
else {
return STF_UNKNOWN;
}
}
TtRssUnsubscribeFeedResponse::TtRssUnsubscribeFeedResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
TtRssUnsubscribeFeedResponse::~TtRssUnsubscribeFeedResponse() = default;
QString TtRssUnsubscribeFeedResponse::code() const {
if (m_rawContent.contains(QSL("content"))) {
QJsonObject map = m_rawContent[QSL("content")].toObject();
if (map.contains(QSL("error"))) {
return map[QSL("error")].toString();
}
else if (map.contains(QSL("status"))) {
return map[QSL("status")].toString();
}
}
return QString();
}
TtRssGetLabelsResponse::TtRssGetLabelsResponse(const QString& raw_content) : TtRssResponse(raw_content) {}
QList<RootItem*> TtRssGetLabelsResponse::labels() const {
QList<RootItem*> labels;
auto json_labels = m_rawContent[QSL("content")].toArray();
for (const QJsonValue& lbl_val : qAsConst(json_labels)) {
QJsonObject lbl_obj = lbl_val.toObject();
Label* lbl = new Label(lbl_obj[QSL("caption")].toString(), QColor(lbl_obj[QSL("fg_color")].toString()));
lbl->setCustomId(QString::number(lbl_obj[QSL("id")].toInt()));
labels.append(lbl);
}
return labels;
}