rssguard/src/services/tt-rss/network/ttrssnetworkfactory.cpp
2015-12-16 19:37:42 +01:00

535 lines
17 KiB
C++
Executable file

// This file is part of RSS Guard.
//
// Copyright (C) 2011-2015 by Martin Rotter <rotter.martinos@gmail.com>
//
// RSS Guard is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// RSS Guard is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with RSS Guard. If not, see <http://www.gnu.org/licenses/>.
#include "services/tt-rss/network/ttrssnetworkfactory.h"
#include "definitions/definitions.h"
#include "services/abstract/rootitem.h"
#include "services/tt-rss/definitions.h"
#include "services/tt-rss/ttrssfeed.h"
#include "services/tt-rss/ttrsscategory.h"
#include "miscellaneous/application.h"
#include "miscellaneous/iconfactory.h"
#include "miscellaneous/textfactory.h"
#include "network-web/networkfactory.h"
#include <QPair>
TtRssNetworkFactory::TtRssNetworkFactory()
: m_url(QString()), m_username(QString()), m_password(QString()), m_forceServerSideUpdate(false), m_authIsUsed(false),
m_authUsername(QString()), m_authPassword(QString()), m_sessionId(QString()),
m_lastLoginTime(QDateTime()), m_lastError(QNetworkReply::NoError) {
}
TtRssNetworkFactory::~TtRssNetworkFactory() {
}
QString TtRssNetworkFactory::url() const {
return m_url;
}
void TtRssNetworkFactory::setUrl(const QString &url) {
m_url = url;
}
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() {
if (!m_sessionId.isEmpty()) {
qDebug("TT-RSS: Session ID is not empty before login, logging out first.");
logout();
}
QtJson::JsonObject json;
json["op"] = "login";
json["user"] = m_username;
json["password"] = m_password;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
TtRssLoginResponse login_response(QString::fromUtf8(result_raw));
if (network_reply.first == QNetworkReply::NoError) {
m_sessionId = login_response.sessionId();
m_lastLoginTime = QDateTime::currentDateTime();
}
else {
qWarning("TT-RSS: Login failed with error %d.", network_reply.first);
}
m_lastError = network_reply.first;
return login_response;
}
TtRssResponse TtRssNetworkFactory::logout() {
if (!m_sessionId.isEmpty()) {
QtJson::JsonObject json;
json["op"] = "logout";
json["sid"] = m_sessionId;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
m_lastError = network_reply.first;
if (m_lastError == QNetworkReply::NoError) {
m_sessionId.clear();
}
else {
qWarning("TT-RSS: Logout failed with error %d.", network_reply.first);
}
return TtRssResponse(QString::fromUtf8(result_raw));
}
else {
qWarning("TT-RSS: Cannot logout because session ID is empty.");
m_lastError = QNetworkReply::NoError;
return TtRssResponse();
}
}
TtRssGetFeedsCategoriesResponse TtRssNetworkFactory::getFeedsCategories() {
QtJson::JsonObject json;
json["op"] = "getFeedTree";
json["sid"] = m_sessionId;
json["include_empty"] = false;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
TtRssGetFeedsCategoriesResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login();
json["sid"] = m_sessionId;
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
result = TtRssGetFeedsCategoriesResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarning("TT-RSS: getFeedTree failed with error %d.", 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) {
QtJson::JsonObject json;
json["op"] = "getHeadlines";
json["sid"] = m_sessionId;
json["feed_id"] = feed_id;
json["force_update"] = m_forceServerSideUpdate;
json["limit"] = limit;
json["skip"] = skip;
json["show_content"] = show_content;
json["include_attachments"] = include_attachments;
json["sanitize"] = sanitize;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
TtRssGetHeadlinesResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login();
json["sid"] = m_sessionId;
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
result = TtRssGetHeadlinesResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarning("TT-RSS: getHeadlines failed with error %d.", network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
TtRssUpdateArticleResponse TtRssNetworkFactory::updateArticles(const QStringList &ids,
UpdateArticle::OperatingField field,
UpdateArticle::Mode mode) {
QtJson::JsonObject json;
json["op"] = "updateArticle";
json["sid"] = m_sessionId;
json["article_ids"] = ids.join(QSL(","));
json["mode"] = (int) mode;
json["field"] = (int) field;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
TtRssUpdateArticleResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login();
json["sid"] = m_sessionId;
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
result = TtRssUpdateArticleResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarning("TT-RSS: updateArticle failed with error %d.", network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
/*
TtRssGetConfigResponse TtRssNetworkFactory::getConfig() {
QtJson::JsonObject json;
json["op"] = "getConfig";
json["sid"] = m_sessionId;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
TtRssGetConfigResponse result(QString::fromUtf8(result_raw));
if (result.isNotLoggedIn()) {
// We are not logged in.
login();
json["sid"] = m_sessionId;
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw,
m_authIsUsed, m_authUsername, m_authPassword);
result = TtRssGetConfigResponse(QString::fromUtf8(result_raw));
}
if (network_reply.first != QNetworkReply::NoError) {
qWarning("TT-RSS: getConfig failed with error %d.", network_reply.first);
}
m_lastError = network_reply.first;
return result;
}
*/
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 = QtJson::parse(raw_content).toMap();
}
TtRssResponse::~TtRssResponse() {
}
bool TtRssResponse::isLoaded() const {
return !m_rawContent.empty();
}
int TtRssResponse::seq() const {
if (!isLoaded()) {
return CONTENT_NOT_LOADED;
}
else {
return m_rawContent["seq"].toInt();
}
}
int TtRssResponse::status() const {
if (!isLoaded()) {
return CONTENT_NOT_LOADED;
}
else {
return m_rawContent["status"].toInt();
}
}
bool TtRssResponse::isNotLoggedIn() const {
return status() == API_STATUS_ERR && hasError() && error() == NOT_LOGGED_IN;
}
TtRssLoginResponse::TtRssLoginResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
TtRssLoginResponse::~TtRssLoginResponse() {
}
int TtRssLoginResponse::apiLevel() const {
if (!isLoaded()) {
return CONTENT_NOT_LOADED;
}
else {
return m_rawContent["content"].toMap()["api_level"].toInt();
}
}
QString TtRssLoginResponse::sessionId() const {
if (!isLoaded()) {
return QString();
}
else {
return m_rawContent["content"].toMap()["session_id"].toString();
}
}
QString TtRssResponse::error() const {
if (!isLoaded()) {
return QString();
}
else {
return m_rawContent["content"].toMap()["error"].toString();
}
}
bool TtRssResponse::hasError() const {
if (!isLoaded()) {
return false;
}
else {
return m_rawContent["content"].toMap().contains("error");
}
}
TtRssGetFeedsCategoriesResponse::TtRssGetFeedsCategoriesResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
TtRssGetFeedsCategoriesResponse::~TtRssGetFeedsCategoriesResponse() {
}
RootItem *TtRssGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons, QString base_address) const {
RootItem *parent = new RootItem();
// Chop the "api/" from the end of the address.
base_address.chop(4);
qDebug("TT-RSS: Chopped base address to '%s' to get feed icons.", qPrintable(base_address));
if (status() == API_STATUS_OK) {
// We have data, construct object tree according to data.
QList<QVariant> items_to_process = m_rawContent["content"].toMap()["categories"].toMap()["items"].toList();
QList<QPair<RootItem*,QVariant> > pairs;
foreach (QVariant item, items_to_process) {
pairs.append(QPair<RootItem*,QVariant>(parent, item));
}
while (!pairs.isEmpty()) {
QPair<RootItem*,QVariant> pair = pairs.takeFirst();
RootItem *act_parent = pair.first;
QMap<QString,QVariant> item = pair.second.toMap();
int item_id = item["bare_id"].toInt();
bool is_category = item.contains("type") && item["type"].toString() == 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("items")) {
foreach (QVariant child_feed, item["items"].toList()) {
pairs.append(QPair<RootItem*,QVariant>(parent, child_feed));
}
}
}
else {
TtRssCategory *category = new TtRssCategory();
category->setTitle(item["name"].toString());
category->setCustomId(item_id);
act_parent->appendChild(category);
if (item.contains("items")) {
foreach (QVariant child, item["items"].toList()) {
pairs.append(QPair<RootItem*,QVariant>(category, child));
}
}
}
}
else {
// We have feed.
TtRssFeed *feed = new TtRssFeed();
if (obtain_icons) {
QString icon_path = item["icon"].type() == QVariant::String ? item["icon"].toString() : QString();
if (!icon_path.isEmpty()) {
// Chop the "api/" suffix out and append
QString full_icon_address = base_address + QL1C('/') + icon_path;
QByteArray icon_data;
if (NetworkFactory::downloadFile(full_icon_address, DOWNLOAD_TIMEOUT, icon_data).first == QNetworkReply::NoError) {
// Icon downloaded, set it up.
QPixmap icon_pixmap;
icon_pixmap.loadFromData(icon_data);
feed->setIcon(QIcon(icon_pixmap));
}
}
}
feed->setTitle(item["name"].toString());
feed->setCustomId(item_id);
act_parent->appendChild(feed);
}
}
}
}
return parent;
}
TtRssGetHeadlinesResponse::TtRssGetHeadlinesResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
TtRssGetHeadlinesResponse::~TtRssGetHeadlinesResponse() {
}
QList<Message> TtRssGetHeadlinesResponse::messages() const {
QList<Message> messages;
foreach (QVariant item, m_rawContent["content"].toList()) {
QMap<QString,QVariant> mapped = item.toMap();
Message message;
message.m_author = mapped["author"].toString();
message.m_isRead = !mapped["unread"].toBool();
message.m_isImportant = mapped["marked"].toBool();
message.m_contents = mapped["content"].toString();
// Multiply by 1000 because Tiny Tiny RSS API does not include miliseconds in Unix
// date/time number.
message.m_created = TextFactory::parseDateTime(mapped["updated"].value<qint64>() * 1000);
message.m_createdFromFeed = true;
message.m_customId = mapped["id"].toString();
message.m_feedId = mapped["feed_id"].toString();
message.m_title = mapped["title"].toString();
message.m_url = mapped["link"].toString();
if (mapped.contains(QSL("attachments"))) {
// Process enclosures.
foreach (QVariant attachment, mapped["attachments"].toList()) {
QMap<QString,QVariant> mapped_attachemnt = attachment.toMap();
Enclosure enclosure;
enclosure.m_mimeType = mapped_attachemnt["content_type"].toString();
enclosure.m_url = mapped_attachemnt["content_url"].toString();
message.m_enclosures.append(enclosure);
}
}
messages.append(message);
}
return messages;
}
TtRssUpdateArticleResponse::TtRssUpdateArticleResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
TtRssUpdateArticleResponse::~TtRssUpdateArticleResponse() {
}
QString TtRssUpdateArticleResponse::updateStatus() const {
if (m_rawContent.contains(QSL("content"))) {
return m_rawContent["content"].toMap()["status"].toString();
}
else {
return QString();
}
}
int TtRssUpdateArticleResponse::articlesUpdated() const {
if (m_rawContent.contains(QSL("content"))) {
return m_rawContent["content"].toMap()["updated"].toInt();
}
else {
return 0;
}
}
/*
TtRssGetConfigResponse::TtRssGetConfigResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
TtRssGetConfigResponse::~TtRssGetConfigResponse() {
}
*/