// This file is part of RSS Guard. // // Copyright (C) 2011-2016 by Martin Rotter // // 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 . #include "services/owncloud/network/owncloudnetworkfactory.h" #include "services/owncloud/definitions.h" #include "network-web/networkfactory.h" #include "miscellaneous/application.h" #include "miscellaneous/settings.h" #include "miscellaneous/textfactory.h" #include "services/abstract/rootitem.h" #include "services/owncloud/owncloudcategory.h" #include "services/owncloud/owncloudfeed.h" #include #include #include #include OwnCloudNetworkFactory::OwnCloudNetworkFactory() : m_url(QString()), m_fixedUrl(QString()), m_forceServerSideUpdate(false), m_authUsername(QString()), m_authPassword(QString()), m_urlUser(QString()), m_urlStatus(QString()), m_urlFolders(QString()), m_urlFeeds(QString()), m_urlMessages(QString()), m_urlFeedsUpdate(QString()), m_urlDeleteFeed(QString()), m_userId(QString()) { } OwnCloudNetworkFactory::~OwnCloudNetworkFactory() { } QString OwnCloudNetworkFactory::url() const { return m_url; } void OwnCloudNetworkFactory::setUrl(const QString &url) { m_url = url; if (url.endsWith('/')) { m_fixedUrl = url; } else { m_fixedUrl = url + '/'; } // Store endpoints. m_urlUser = m_fixedUrl + API_PATH + "user"; m_urlStatus = m_fixedUrl + API_PATH + "status"; m_urlFolders = m_fixedUrl + API_PATH + "folders"; m_urlFeeds = m_fixedUrl + API_PATH + "feeds"; m_urlMessages = m_fixedUrl + API_PATH + "items?id=%1&batchSize=%2&type=%3"; m_urlFeedsUpdate = m_fixedUrl + API_PATH + "feeds/update?userId=%1&feedId=%2"; m_urlDeleteFeed = m_fixedUrl + API_PATH + "feeds/%1"; setUserId(QString()); } bool OwnCloudNetworkFactory::forceServerSideUpdate() const { return m_forceServerSideUpdate; } void OwnCloudNetworkFactory::setForceServerSideUpdate(bool force_update) { m_forceServerSideUpdate = force_update; } QString OwnCloudNetworkFactory::authUsername() const { return m_authUsername; } void OwnCloudNetworkFactory::setAuthUsername(const QString &auth_username) { m_authUsername = auth_username; setUserId(QString()); } QString OwnCloudNetworkFactory::authPassword() const { return m_authPassword; } void OwnCloudNetworkFactory::setAuthPassword(const QString &auth_password) { m_authPassword = auth_password; setUserId(QString()); } QNetworkReply::NetworkError OwnCloudNetworkFactory::lastError() const { return m_lastError; } OwnCloudUserResponse OwnCloudNetworkFactory::userInfo() { QByteArray result_raw; NetworkResult network_reply = NetworkFactory::downloadFile(m_urlUser, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), result_raw, true, m_authUsername, m_authPassword, true); OwnCloudUserResponse user_response(QString::fromUtf8(result_raw)); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Obtaining user info failed with error %d.", network_reply.first); } m_lastError = network_reply.first; return user_response; } OwnCloudStatusResponse OwnCloudNetworkFactory::status() { QByteArray result_raw; NetworkResult network_reply = NetworkFactory::downloadFile(m_urlStatus, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), result_raw, true, m_authUsername, m_authPassword, true); OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw)); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Obtaining status info failed with error %d.", network_reply.first); } m_lastError = network_reply.first; return status_response; } OwnCloudGetFeedsCategoriesResponse OwnCloudNetworkFactory::feedsCategories() { QByteArray result_raw; NetworkResult network_reply = NetworkFactory::downloadFile(m_urlFolders, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), result_raw, true, m_authUsername, m_authPassword, true); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first); m_lastError = network_reply.first; return OwnCloudGetFeedsCategoriesResponse(); } QString content_categories = QString::fromUtf8(result_raw); // Now, obtain feeds. network_reply = NetworkFactory::downloadFile(m_urlFeeds, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), result_raw, true, m_authUsername, m_authPassword, true); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Obtaining of feeds failed with error %d.", network_reply.first); m_lastError = network_reply.first; return OwnCloudGetFeedsCategoriesResponse(); } QString content_feeds = QString::fromUtf8(result_raw); m_lastError = network_reply.first; return OwnCloudGetFeedsCategoriesResponse(content_categories, content_feeds); } bool OwnCloudNetworkFactory::deleteFeed(int feed_id) { QString final_url = m_urlDeleteFeed.arg(QString::number(feed_id)); NetworkResult network_reply = NetworkFactory::deleteResource(final_url, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), true, m_authUsername, m_authPassword, true); m_lastError = network_reply.first; if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first); return false; } else { return true; } } OwnCloudGetMessagesResponse OwnCloudNetworkFactory::getMessages(int feed_id) { if (forceServerSideUpdate()) { triggerFeedUpdate(feed_id); } QString final_url = m_urlMessages.arg(QString::number(feed_id), QString::number(-1), QString::number(0)); QByteArray result_raw; NetworkResult network_reply = NetworkFactory::downloadFile(final_url, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), result_raw, true, m_authUsername, m_authPassword, true); OwnCloudGetMessagesResponse msgs_response(QString::fromUtf8(result_raw)); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Obtaining messages failed with error %d.", network_reply.first); } m_lastError = network_reply.first; return msgs_response; } QNetworkReply::NetworkError OwnCloudNetworkFactory::triggerFeedUpdate(int feed_id) { if (userId().isEmpty()) { // We need to get user ID first. OwnCloudUserResponse info = userInfo(); if (lastError() != QNetworkReply::NoError) { return lastError(); } else { // We have new user ID, set it up. setUserId(info.userId()); } } // Now, we can trigger the update. QByteArray raw_output; NetworkResult network_reply = NetworkFactory::downloadFile(m_urlFeedsUpdate.arg(userId(), QString::number(feed_id)), qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), raw_output, true, m_authUsername, m_authPassword, true); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Feeds update failed with error %d.", network_reply.first); } return (m_lastError = network_reply.first); } QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesRead(RootItem::ReadStatus status, const QStringList &custom_ids) { QList var_ids; QJsonObject json; QJsonArray ids; QByteArray raw_output; QString final_url; if (status == RootItem::Read) { final_url = m_fixedUrl + API_PATH + "items/read/multiple"; } else { final_url = m_fixedUrl + API_PATH + "items/unread/multiple"; } foreach (const QString &id, custom_ids) { var_ids.append(id.toInt()); } ids.append(QJsonArray::fromVariantList(var_ids)); json["items"] = ids; NetworkResult network_reply = NetworkFactory::uploadData(final_url, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), QJsonDocument(json).toJson(QJsonDocument::Compact), "application/json", raw_output, QNetworkAccessManager::PutOperation, true, m_authUsername, m_authPassword, true); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Marking messages as (un)read failed with error %d.", network_reply.first); } return (m_lastError = network_reply.first); } QNetworkReply::NetworkError OwnCloudNetworkFactory::markMessagesStarred(RootItem::Importance importance, const QStringList &feed_ids, const QStringList &guid_hashes) { QJsonObject json; QJsonArray ids; QByteArray raw_output; QString final_url; if (importance == RootItem::Important) { final_url = m_fixedUrl + API_PATH + "items/star/multiple"; } else { final_url = m_fixedUrl + API_PATH + "items/unstar/multiple"; } for (int i = 0; i < feed_ids.size(); i++) { QVariantMap item; item.insert(QSL("feedId"), feed_ids.at(i)); item.insert(QSL("guidHash"), guid_hashes.at(i)); ids.append(QJsonValue::fromVariant(item)); } json["items"] = ids; NetworkResult network_reply = NetworkFactory::uploadData(final_url, qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(), QJsonDocument(json).toJson(QJsonDocument::Compact), "application/json", raw_output, QNetworkAccessManager::PutOperation, true, m_authUsername, m_authPassword, true); if (network_reply.first != QNetworkReply::NoError) { qWarning("ownCloud: Marking messages as (un)starred failed with error %d.", network_reply.first); } return (m_lastError = network_reply.first); } QString OwnCloudNetworkFactory::userId() const { return m_userId; } void OwnCloudNetworkFactory::setUserId(const QString &userId) { m_userId = userId; } OwnCloudResponse::OwnCloudResponse(const QString &raw_content) { m_rawContent = QJsonDocument::fromJson(raw_content.toUtf8()).object(); } OwnCloudResponse::~OwnCloudResponse() { } bool OwnCloudResponse::isLoaded() const { return !m_rawContent.isEmpty(); } QString OwnCloudResponse::toString() const { return QJsonDocument(m_rawContent).toJson(QJsonDocument::Compact); } OwnCloudUserResponse::OwnCloudUserResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { } OwnCloudUserResponse::~OwnCloudUserResponse() { } QString OwnCloudUserResponse::displayName() const { if (isLoaded()) { return m_rawContent["displayName"].toString(); } else { return QString(); } } QString OwnCloudUserResponse::userId() const { if (isLoaded()) { return m_rawContent["userId"].toString(); } else { return QString(); } } QDateTime OwnCloudUserResponse::lastLoginTime() const { if (isLoaded()) { return QDateTime::fromMSecsSinceEpoch(m_rawContent["lastLoginTimestamp"].toVariant().value()); } else { return QDateTime(); } } QIcon OwnCloudUserResponse::avatar() const { if (isLoaded()) { QString image_data = m_rawContent["avatar"].toObject()["data"].toString(); QByteArray decoded_data = QByteArray::fromBase64(image_data.toLocal8Bit()); QPixmap image; if (image.loadFromData(decoded_data)) { return QIcon(image); } } return QIcon(); } OwnCloudStatusResponse::OwnCloudStatusResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { } OwnCloudStatusResponse::~OwnCloudStatusResponse() { } QString OwnCloudStatusResponse::version() const { if (isLoaded()) { return m_rawContent["version"].toString(); } else { return QString(); } } bool OwnCloudStatusResponse::misconfiguredCron() const { if (isLoaded()) { return m_rawContent["warnings"].toObject()["improperlyConfiguredCron"].toBool(); } else { return false; } } OwnCloudGetFeedsCategoriesResponse::OwnCloudGetFeedsCategoriesResponse(const QString &raw_categories, const QString &raw_feeds) : m_contentCategories(raw_categories), m_contentFeeds(raw_feeds) { } OwnCloudGetFeedsCategoriesResponse::~OwnCloudGetFeedsCategoriesResponse() { } RootItem *OwnCloudGetFeedsCategoriesResponse::feedsCategories(bool obtain_icons) const { RootItem *parent = new RootItem(); QMap cats; cats.insert(0, parent); // Process categories first, then process feeds. foreach (QVariant cat, QJsonDocument::fromJson(m_contentCategories.toUtf8()).object()["folders"].toArray().toVariantList()) { QMap item = cat.toMap(); OwnCloudCategory *category = new OwnCloudCategory(); category->setTitle(item["name"].toString()); category->setCustomId(item["id"].toInt()); cats.insert(category->customId(), category); // All categories in ownCloud are top-level. parent->appendChild(category); } // We have categories added, now add all feeds. foreach (QVariant fed, QJsonDocument::fromJson(m_contentFeeds.toUtf8()).object()["feeds"].toArray().toVariantList()) { QMap item = fed.toMap(); OwnCloudFeed *feed = new OwnCloudFeed(); if (obtain_icons) { QString icon_path = item["faviconLink"].toString(); if (!icon_path.isEmpty()) { QByteArray icon_data; if (NetworkFactory::downloadFile(icon_path, 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->setUrl(item["link"].toString()); feed->setTitle(item["title"].toString()); feed->setCustomId(item["id"].toInt()); cats.value(item["folderId"].toInt())->appendChild(feed); } return parent; } OwnCloudGetMessagesResponse::OwnCloudGetMessagesResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { } OwnCloudGetMessagesResponse::~OwnCloudGetMessagesResponse() { } QList OwnCloudGetMessagesResponse::messages() const { QList msgs; foreach (QJsonValue message, m_rawContent["items"].toArray()) { QJsonObject message_map = message.toObject(); Message msg; msg.m_author = message_map["author"].toString(); msg.m_contents = message_map["body"].toString(); msg.m_created = TextFactory::parseDateTime(message_map["pubDate"].toVariant().value() * 1000); msg.m_createdFromFeed = true; msg.m_customId = message_map["id"].toString(); msg.m_customHash = message_map["guidHash"].toString(); QString enclosure_link = message_map["enclosureLink"].toString(); if (!enclosure_link.isEmpty()) { Enclosure enclosure; enclosure.m_mimeType = message_map["enclosureMime"].toString(); enclosure.m_url = enclosure_link; msg.m_enclosures.append(enclosure); } msg.m_feedId = message_map["feedId"].toString(); msg.m_isImportant = message_map["starred"].toBool(); msg.m_isRead = !message_map["unread"].toBool(); msg.m_title = message_map["title"].toString(); msg.m_url = message_map["url"].toString(); msgs.append(msg); } return msgs; }