more logging for #730
This commit is contained in:
parent
d1b904de94
commit
76912b6d80
@ -26,7 +26,7 @@
|
||||
<url type="donation">https://github.com/sponsors/martinrotter</url>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="4.2.2" date="2022-05-06"/>
|
||||
<release version="4.2.2" date="2022-05-10"/>
|
||||
</releases>
|
||||
<content_rating type="oars-1.0">
|
||||
<content_attribute id="violence-cartoon">none</content_attribute>
|
||||
|
@ -42,12 +42,16 @@
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
OAuth2Service::OAuth2Service(const QString& auth_url, const QString& token_url, const QString& client_id,
|
||||
const QString& client_secret, const QString& scope, QObject* parent)
|
||||
: QObject(parent),
|
||||
m_id(QString::number(QRandomGenerator::global()->generate())), m_timerId(-1),
|
||||
m_redirectionHandler(new OAuthHttpHandler(tr("You can close this window now. Go back to %1.").arg(QSL(APP_NAME)), this)),
|
||||
m_functorOnLogin(std::function<void()>()) {
|
||||
OAuth2Service::OAuth2Service(const QString& auth_url,
|
||||
const QString& token_url,
|
||||
const QString& client_id,
|
||||
const QString& client_secret,
|
||||
const QString& scope,
|
||||
QObject* parent)
|
||||
: QObject(parent), m_id(QString::number(QRandomGenerator::global()->generate())), m_timerId(-1),
|
||||
m_redirectionHandler(new OAuthHttpHandler(tr("You can close this window now. Go back to %1.").arg(QSL(APP_NAME)),
|
||||
this)),
|
||||
m_functorOnLogin(std::function<void()>()) {
|
||||
m_tokenGrantType = QSL("authorization_code");
|
||||
m_tokenUrl = QUrl(token_url);
|
||||
m_authUrl = auth_url;
|
||||
@ -65,14 +69,16 @@ OAuth2Service::OAuth2Service(const QString& auth_url, const QString& token_url,
|
||||
retrieveAccessToken(auth_code);
|
||||
}
|
||||
});
|
||||
connect(m_redirectionHandler, &OAuthHttpHandler::authRejected, [this](const QString& error_description, const QString& id) {
|
||||
Q_UNUSED(error_description)
|
||||
connect(m_redirectionHandler,
|
||||
&OAuthHttpHandler::authRejected,
|
||||
[this](const QString& error_description, const QString& id) {
|
||||
Q_UNUSED(error_description)
|
||||
|
||||
if (id.isEmpty() || id == m_id) {
|
||||
// We process this further only if handler (static singleton) responded to our original request.
|
||||
emit authFailed();
|
||||
}
|
||||
});
|
||||
if (id.isEmpty() || id == m_id) {
|
||||
// We process this further only if handler (static singleton) responded to our original request.
|
||||
emit authFailed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
OAuth2Service::~OAuth2Service() {
|
||||
@ -81,15 +87,14 @@ OAuth2Service::~OAuth2Service() {
|
||||
|
||||
QString OAuth2Service::bearer() {
|
||||
if (!isFullyLoggedIn()) {
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure, {
|
||||
tr("You have to login first"),
|
||||
tr("Click here to login."),
|
||||
QSystemTrayIcon::MessageIcon::Critical },
|
||||
{}, {
|
||||
tr("Login"),
|
||||
[this]() {
|
||||
login();
|
||||
} });
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure,
|
||||
{tr("You have to login first"),
|
||||
tr("Click here to login."),
|
||||
QSystemTrayIcon::MessageIcon::Critical},
|
||||
{},
|
||||
{tr("Login"), [this]() {
|
||||
login();
|
||||
}});
|
||||
return {};
|
||||
}
|
||||
else {
|
||||
@ -179,11 +184,12 @@ void OAuth2Service::retrieveAccessToken(const QString& auth_code) {
|
||||
"client_secret=%2&"
|
||||
"code=%3&"
|
||||
"redirect_uri=%5&"
|
||||
"grant_type=%4").arg(properClientId(),
|
||||
properClientSecret(),
|
||||
auth_code,
|
||||
m_tokenGrantType,
|
||||
m_redirectionHandler->listenAddressPort());
|
||||
"grant_type=%4")
|
||||
.arg(properClientId(),
|
||||
properClientSecret(),
|
||||
auth_code,
|
||||
m_tokenGrantType,
|
||||
m_redirectionHandler->listenAddressPort());
|
||||
|
||||
qDebugNN << LOGSEC_OAUTH << "Posting data for access token retrieval:" << QUOTE_W_SPACE_DOT(content);
|
||||
m_networkManager.post(network_request, content.toUtf8());
|
||||
@ -205,16 +211,14 @@ void OAuth2Service::refreshAccessToken(const QString& refresh_token) {
|
||||
QString content = QString("client_id=%1&"
|
||||
"client_secret=%2&"
|
||||
"refresh_token=%3&"
|
||||
"grant_type=%4").arg(properClientId(),
|
||||
properClientSecret(),
|
||||
real_refresh_token,
|
||||
QSL("refresh_token"));
|
||||
"grant_type=%4")
|
||||
.arg(properClientId(), properClientSecret(), real_refresh_token, QSL("refresh_token"));
|
||||
|
||||
qApp->showGuiMessage(Notification::Event::LoginDataRefreshed, {
|
||||
tr("Logging in via OAuth 2.0..."),
|
||||
tr("Refreshing login tokens for '%1'...").arg(m_tokenUrl.toString()),
|
||||
QSystemTrayIcon::MessageIcon::Information },
|
||||
{ true, false, true });
|
||||
qApp->showGuiMessage(Notification::Event::LoginDataRefreshed,
|
||||
{tr("Logging in via OAuth 2.0..."),
|
||||
tr("Refreshing login tokens for '%1'...").arg(m_tokenUrl.toString()),
|
||||
QSystemTrayIcon::MessageIcon::Information},
|
||||
{true, false, true});
|
||||
|
||||
qDebugNN << LOGSEC_OAUTH << "Posting data for access token refreshing:" << QUOTE_W_SPACE_DOT(content);
|
||||
m_networkManager.post(network_request, content.toUtf8());
|
||||
@ -229,8 +233,7 @@ void OAuth2Service::tokenRequestFinished(QNetworkReply* network_reply) {
|
||||
|
||||
if (network_reply->error() != QNetworkReply::NetworkError::NoError) {
|
||||
qWarningNN << LOGSEC_OAUTH
|
||||
<< "Network error when obtaining token response:"
|
||||
<< QUOTE_W_SPACE_DOT(network_reply->error());
|
||||
<< "Network error when obtaining token response:" << QUOTE_W_SPACE_DOT(network_reply->error());
|
||||
|
||||
emit tokensRetrieveError(QString(), NetworkFactory::networkErrorText(network_reply->error()));
|
||||
}
|
||||
@ -238,9 +241,7 @@ void OAuth2Service::tokenRequestFinished(QNetworkReply* network_reply) {
|
||||
QString error = root_obj.value(QSL("error")).toString();
|
||||
QString error_description = root_obj.value(QSL("error_description")).toString();
|
||||
|
||||
qWarningNN << LOGSEC_OAUTH
|
||||
<< "JSON error when obtaining token response:"
|
||||
<< QUOTE_W_SPACE(error)
|
||||
qWarningNN << LOGSEC_OAUTH << "JSON error when obtaining token response:" << QUOTE_W_SPACE(error)
|
||||
<< QUOTE_W_SPACE_DOT(error_description);
|
||||
|
||||
logout();
|
||||
@ -259,11 +260,12 @@ void OAuth2Service::tokenRequestFinished(QNetworkReply* network_reply) {
|
||||
setRefreshToken(refresh_token);
|
||||
}
|
||||
|
||||
qDebugNN << LOGSEC_OAUTH
|
||||
<< "Obtained refresh token" << QUOTE_W_SPACE(refreshToken())
|
||||
<< "- expires on date/time" << QUOTE_W_SPACE_DOT(tokensExpireIn());
|
||||
qDebugNN << LOGSEC_OAUTH << "Obtained refresh token" << QUOTE_W_SPACE(refreshToken()) << "- expires on date/time"
|
||||
<< QUOTE_W_SPACE_DOT(tokensExpireIn());
|
||||
|
||||
if (m_functorOnLogin != nullptr) {
|
||||
qDebugNN << LOGSEC_OAUTH << "Running custom after-login code.";
|
||||
|
||||
m_functorOnLogin();
|
||||
}
|
||||
|
||||
@ -274,15 +276,11 @@ void OAuth2Service::tokenRequestFinished(QNetworkReply* network_reply) {
|
||||
}
|
||||
|
||||
QString OAuth2Service::properClientId() const {
|
||||
return m_clientId.simplified().isEmpty()
|
||||
? m_clientSecretId
|
||||
: m_clientId;
|
||||
return m_clientId.simplified().isEmpty() ? m_clientSecretId : m_clientId;
|
||||
}
|
||||
|
||||
QString OAuth2Service::properClientSecret() const {
|
||||
return m_clientSecret.simplified().isEmpty()
|
||||
? m_clientSecretSecret
|
||||
: m_clientSecret;
|
||||
return m_clientSecret.simplified().isEmpty() ? m_clientSecretSecret : m_clientSecret;
|
||||
}
|
||||
|
||||
QString OAuth2Service::accessToken() const {
|
||||
@ -339,12 +337,12 @@ bool OAuth2Service::login(const std::function<void()>& functor_when_logged_in) {
|
||||
m_functorOnLogin = functor_when_logged_in;
|
||||
|
||||
if (!m_redirectionHandler->isListening()) {
|
||||
qCriticalNN << LOGSEC_OAUTH
|
||||
<< "Cannot log-in because OAuth redirection handler is not listening.";
|
||||
qCriticalNN << LOGSEC_OAUTH << "Cannot log-in because OAuth redirection handler is not listening.";
|
||||
|
||||
emit tokensRetrieveError(QString(), tr("Failed to start OAuth "
|
||||
"redirection listener. Maybe your "
|
||||
"rights are not high enough."));
|
||||
emit tokensRetrieveError(QString(),
|
||||
tr("Failed to start OAuth "
|
||||
"redirection listener. Maybe your "
|
||||
"rights are not high enough."));
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -402,10 +400,8 @@ void OAuth2Service::retrieveAuthCode() {
|
||||
"state=%4&"
|
||||
"prompt=consent&"
|
||||
"duration=permanent&"
|
||||
"access_type=offline").arg(properClientId(),
|
||||
m_scope,
|
||||
m_redirectionHandler->listenAddressPort(),
|
||||
m_id);
|
||||
"access_type=offline")
|
||||
.arg(properClientId(), m_scope, m_redirectionHandler->listenAddressPort(), m_id);
|
||||
|
||||
// We run login URL in external browser, response is caught by light HTTP server.
|
||||
qApp->web()->openUrlInExternalBrowser(auth_url);
|
||||
|
@ -23,8 +23,8 @@
|
||||
|
||||
ServiceRoot::ServiceRoot(RootItem* parent)
|
||||
: RootItem(parent), m_recycleBin(new RecycleBin(this)), m_importantNode(new ImportantNode(this)),
|
||||
m_labelsNode(new LabelsNode(this)), m_unreadNode(new UnreadNode(this)),
|
||||
m_accountId(NO_PARENT_CATEGORY), m_networkProxy(QNetworkProxy()) {
|
||||
m_labelsNode(new LabelsNode(this)), m_unreadNode(new UnreadNode(this)), m_accountId(NO_PARENT_CATEGORY),
|
||||
m_networkProxy(QNetworkProxy()) {
|
||||
setKind(RootItem::Kind::ServiceRoot);
|
||||
appendCommonNodes();
|
||||
}
|
||||
@ -95,7 +95,8 @@ QList<QAction*> ServiceRoot::contextMenuMessagesList(const QList<Message>& messa
|
||||
QList<QAction*> ServiceRoot::serviceMenu() {
|
||||
if (m_serviceMenu.isEmpty()) {
|
||||
if (isSyncable()) {
|
||||
auto* act_sync_tree = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Synchronize folders && other items"), this);
|
||||
auto* act_sync_tree =
|
||||
new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Synchronize folders && other items"), this);
|
||||
|
||||
connect(act_sync_tree, &QAction::triggered, this, &ServiceRoot::syncIn);
|
||||
m_serviceMenu.append(act_sync_tree);
|
||||
@ -103,7 +104,8 @@ QList<QAction*> ServiceRoot::serviceMenu() {
|
||||
auto* cache = toCache();
|
||||
|
||||
if (cache != nullptr) {
|
||||
auto* act_sync_cache = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Synchronize article cache"), this);
|
||||
auto* act_sync_cache =
|
||||
new QAction(qApp->icons()->fromTheme(QSL("view-refresh")), tr("Synchronize article cache"), this);
|
||||
|
||||
connect(act_sync_cache, &QAction::triggered, this, [cache]() {
|
||||
cache->saveAllCachedData(false);
|
||||
@ -139,8 +141,7 @@ void ServiceRoot::updateCounts(bool including_total_count) {
|
||||
if (child->kind() == RootItem::Kind::Feed) {
|
||||
feeds.append(child->toFeed());
|
||||
}
|
||||
else if (child->kind() != RootItem::Kind::Labels &&
|
||||
child->kind() != RootItem::Kind::Category &&
|
||||
else if (child->kind() != RootItem::Kind::Labels && child->kind() != RootItem::Kind::Category &&
|
||||
child->kind() != RootItem::Kind::ServiceRoot) {
|
||||
child->updateCounts(including_total_count);
|
||||
}
|
||||
@ -152,7 +153,8 @@ void ServiceRoot::updateCounts(bool including_total_count) {
|
||||
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||
bool ok;
|
||||
QMap<QString, QPair<int, int>> counts = DatabaseQueries::getMessageCountsForAccount(database, accountId(), including_total_count, &ok);
|
||||
QMap<QString, QPair<int, int>> counts =
|
||||
DatabaseQueries::getMessageCountsForAccount(database, accountId(), including_total_count, &ok);
|
||||
|
||||
if (ok) {
|
||||
for (Feed* feed : feeds) {
|
||||
@ -183,7 +185,7 @@ void ServiceRoot::completelyRemoveAllData() {
|
||||
cleanAllItemsFromModel(true);
|
||||
removeOldAccountFromDatabase(true, true);
|
||||
updateCounts(true);
|
||||
itemChanged({ this });
|
||||
itemChanged({this});
|
||||
requestReloadMessageList(true);
|
||||
}
|
||||
|
||||
@ -204,20 +206,15 @@ QIcon ServiceRoot::feedIconForMessage(const QString& feed_custom_id) const {
|
||||
void ServiceRoot::removeOldAccountFromDatabase(bool delete_messages_too, bool delete_labels_too) {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||
|
||||
DatabaseQueries::deleteAccountData(database,
|
||||
accountId(),
|
||||
delete_messages_too,
|
||||
delete_labels_too);
|
||||
DatabaseQueries::deleteAccountData(database, accountId(), delete_messages_too, delete_labels_too);
|
||||
}
|
||||
|
||||
void ServiceRoot::cleanAllItemsFromModel(bool clean_labels_too) {
|
||||
auto chi = childItems();
|
||||
|
||||
for (RootItem* top_level_item : qAsConst(chi)) {
|
||||
if (top_level_item->kind() != RootItem::Kind::Bin &&
|
||||
top_level_item->kind() != RootItem::Kind::Important &&
|
||||
top_level_item->kind() != RootItem::Kind::Unread &&
|
||||
top_level_item->kind() != RootItem::Kind::Labels) {
|
||||
if (top_level_item->kind() != RootItem::Kind::Bin && top_level_item->kind() != RootItem::Kind::Important &&
|
||||
top_level_item->kind() != RootItem::Kind::Unread && top_level_item->kind() != RootItem::Kind::Labels) {
|
||||
requestItemRemoval(top_level_item);
|
||||
}
|
||||
}
|
||||
@ -265,7 +262,9 @@ bool ServiceRoot::cleanFeeds(const QList<Feed*>& items, bool clean_read_only) {
|
||||
|
||||
void ServiceRoot::storeNewFeedTree(RootItem* root) {
|
||||
try {
|
||||
DatabaseQueries::storeAccountTree(qApp->database()->driver()->connection(metaObject()->className()), root, accountId());
|
||||
DatabaseQueries::storeAccountTree(qApp->database()->driver()->connection(metaObject()->className()),
|
||||
root,
|
||||
accountId());
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
qFatal("Cannot store account tree: '%s'.", qPrintable(ex.message()));
|
||||
@ -405,7 +404,8 @@ void ServiceRoot::restoreCustomFeedsData(const QMap<QString, QVariantMap>& data,
|
||||
QVariantMap feed_custom_data = i.value();
|
||||
|
||||
feed->setAutoUpdateInitialInterval(feed_custom_data.value(QSL("auto_update_interval")).toInt());
|
||||
feed->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(feed_custom_data.value(QSL("auto_update_type")).toInt()));
|
||||
feed
|
||||
->setAutoUpdateType(static_cast<Feed::AutoUpdateType>(feed_custom_data.value(QSL("auto_update_type")).toInt()));
|
||||
feed->setMessageFilters(feed_custom_data.value(QSL("msg_filters")).value<QList<QPointer<MessageFilter>>>());
|
||||
|
||||
feed->setIsSwitchedOff(feed_custom_data.value(QSL("is_off")).toBool());
|
||||
@ -440,14 +440,20 @@ void ServiceRoot::syncIn() {
|
||||
QIcon original_icon = icon();
|
||||
|
||||
setIcon(qApp->icons()->fromTheme(QSL("view-refresh")));
|
||||
itemChanged({ this });
|
||||
itemChanged({this});
|
||||
|
||||
qDebugNN << LOGSEC_CORE << "Starting sync-in process.";
|
||||
|
||||
RootItem* new_tree = obtainNewTreeForSyncIn();
|
||||
|
||||
if (new_tree != nullptr) {
|
||||
qDebugNN << LOGSEC_CORE << "New feed tree for sync-in obtained.";
|
||||
|
||||
auto feed_custom_data = storeCustomFeedsData();
|
||||
|
||||
// Remove from feeds model, then from SQL but leave messages intact.
|
||||
bool uses_remote_labels = (supportedLabelOperations() & LabelOperation::Synchronised) == LabelOperation::Synchronised;
|
||||
bool uses_remote_labels =
|
||||
(supportedLabelOperations() & LabelOperation::Synchronised) == LabelOperation::Synchronised;
|
||||
|
||||
// Remove stuff.
|
||||
cleanAllItemsFromModel(uses_remote_labels);
|
||||
@ -492,6 +498,9 @@ void ServiceRoot::syncIn() {
|
||||
updateCounts(true);
|
||||
requestReloadMessageList(true);
|
||||
}
|
||||
else {
|
||||
qCriticalNN << LOGSEC_CORE << "New feed tree for sync-in NOT obtained.";
|
||||
}
|
||||
|
||||
setIcon(original_icon);
|
||||
itemChanged(getSubTree());
|
||||
@ -609,7 +618,8 @@ QStringList ServiceRoot::textualFeedUrls(const QList<Feed*>& feeds) const {
|
||||
}
|
||||
|
||||
QStringList ServiceRoot::textualFeedIds(const QList<Feed*>& feeds) const {
|
||||
QStringList stringy_ids; stringy_ids.reserve(feeds.size());
|
||||
QStringList stringy_ids;
|
||||
stringy_ids.reserve(feeds.size());
|
||||
|
||||
for (const Feed* feed : feeds) {
|
||||
stringy_ids.append(QSL("'%1'").arg(feed->customId()));
|
||||
@ -619,7 +629,8 @@ QStringList ServiceRoot::textualFeedIds(const QList<Feed*>& feeds) const {
|
||||
}
|
||||
|
||||
QStringList ServiceRoot::customIDsOfMessages(const QList<ImportanceChange>& changes) {
|
||||
QStringList list; list.reserve(changes.size());
|
||||
QStringList list;
|
||||
list.reserve(changes.size());
|
||||
|
||||
for (const auto& change : changes) {
|
||||
list.append(change.first.m_customId);
|
||||
@ -629,7 +640,8 @@ QStringList ServiceRoot::customIDsOfMessages(const QList<ImportanceChange>& chan
|
||||
}
|
||||
|
||||
QStringList ServiceRoot::customIDsOfMessages(const QList<Message>& messages) {
|
||||
QStringList list; list.reserve(messages.size());
|
||||
QStringList list;
|
||||
list.reserve(messages.size());
|
||||
|
||||
for (const Message& message : messages) {
|
||||
list.append(message.m_customId);
|
||||
@ -655,32 +667,35 @@ void ServiceRoot::setAccountId(int account_id) {
|
||||
bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) {
|
||||
if (item->kind() == RootItem::Kind::Bin) {
|
||||
model->setFilter(QSL("Messages.is_deleted = 1 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1")
|
||||
.arg(QString::number(accountId())));
|
||||
.arg(QString::number(accountId())));
|
||||
}
|
||||
else if (item->kind() == RootItem::Kind::Important) {
|
||||
model->setFilter(QSL("Messages.is_important = 1 AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1")
|
||||
.arg(QString::number(accountId())));
|
||||
model->setFilter(QSL("Messages.is_important = 1 AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND "
|
||||
"Messages.account_id = %1")
|
||||
.arg(QString::number(accountId())));
|
||||
}
|
||||
else if (item->kind() == RootItem::Kind::Unread) {
|
||||
model->setFilter(QSL("Messages.is_read = 0 AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1")
|
||||
.arg(QString::number(accountId())));
|
||||
model->setFilter(QSL("Messages.is_read = 0 AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND "
|
||||
"Messages.account_id = %1")
|
||||
.arg(QString::number(accountId())));
|
||||
}
|
||||
else if (item->kind() == RootItem::Kind::Label) {
|
||||
// Show messages with particular label.
|
||||
model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1 AND "
|
||||
"(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = Messages.custom_id AND label = '%2') > 0")
|
||||
.arg(QString::number(accountId()), item->customId()));
|
||||
"(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = "
|
||||
"Messages.custom_id AND label = '%2') > 0")
|
||||
.arg(QString::number(accountId()), item->customId()));
|
||||
}
|
||||
else if (item->kind() == RootItem::Kind::Labels) {
|
||||
// Show messages with any label.
|
||||
model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1 AND "
|
||||
"(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = Messages.custom_id) > 0")
|
||||
.arg(QString::number(accountId())));
|
||||
"(SELECT COUNT(*) FROM LabelsInMessages WHERE account_id = %1 AND message = "
|
||||
"Messages.custom_id) > 0")
|
||||
.arg(QString::number(accountId())));
|
||||
}
|
||||
else if (item->kind() == RootItem::Kind::ServiceRoot) {
|
||||
model->setFilter(
|
||||
QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1").arg(
|
||||
QString::number(accountId())));
|
||||
model->setFilter(QSL("Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %1")
|
||||
.arg(QString::number(accountId())));
|
||||
|
||||
qDebugNN << "Displaying messages from account:" << QUOTE_W_SPACE_DOT(accountId());
|
||||
}
|
||||
@ -692,11 +707,9 @@ bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) {
|
||||
filter_clause = QSL("null");
|
||||
}
|
||||
|
||||
model->setFilter(
|
||||
QSL("Feeds.custom_id IN (%1) AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND Messages.account_id = %2").arg(
|
||||
filter_clause,
|
||||
QString::
|
||||
number(accountId())));
|
||||
model->setFilter(QSL("Feeds.custom_id IN (%1) AND Messages.is_deleted = 0 AND Messages.is_pdeleted = 0 AND "
|
||||
"Messages.account_id = %2")
|
||||
.arg(filter_clause, QString::number(accountId())));
|
||||
|
||||
QString urls = textualFeedUrls(children).join(QSL(", "));
|
||||
|
||||
@ -707,7 +720,9 @@ bool ServiceRoot::loadMessagesForItem(RootItem* item, MessagesModel* model) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item, const QList<Message>& messages, RootItem::ReadStatus read) {
|
||||
bool ServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item,
|
||||
const QList<Message>& messages,
|
||||
RootItem::ReadStatus read) {
|
||||
Q_UNUSED(selected_item)
|
||||
|
||||
auto cache = dynamic_cast<CacheForServiceRoot*>(this);
|
||||
@ -719,7 +734,9 @@ bool ServiceRoot::onBeforeSetMessagesRead(RootItem* selected_item, const QList<M
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServiceRoot::onAfterSetMessagesRead(RootItem* selected_item, const QList<Message>& messages, RootItem::ReadStatus read) {
|
||||
bool ServiceRoot::onAfterSetMessagesRead(RootItem* selected_item,
|
||||
const QList<Message>& messages,
|
||||
RootItem::ReadStatus read) {
|
||||
Q_UNUSED(selected_item)
|
||||
Q_UNUSED(messages)
|
||||
Q_UNUSED(read)
|
||||
@ -808,9 +825,11 @@ bool ServiceRoot::onAfterLabelMessageAssignmentChanged(const QList<Label*>& labe
|
||||
lbl->updateCounts(true);
|
||||
});
|
||||
|
||||
auto list = boolinq::from(labels).select([](Label* lbl) {
|
||||
return static_cast<RootItem*>(lbl);
|
||||
}).toStdList();
|
||||
auto list = boolinq::from(labels)
|
||||
.select([](Label* lbl) {
|
||||
return static_cast<RootItem*>(lbl);
|
||||
})
|
||||
.toStdList();
|
||||
|
||||
getParentServiceRoot()->itemChanged(FROM_STD_LIST(QList<RootItem*>, list));
|
||||
return true;
|
||||
@ -888,25 +907,21 @@ ServiceRoot::LabelOperation operator&(ServiceRoot::LabelOperation lhs, ServiceRo
|
||||
}
|
||||
|
||||
QPair<int, int> ServiceRoot::updateMessages(QList<Message>& messages, Feed* feed, bool force_update) {
|
||||
QPair<int, int> updated_messages = { 0, 0 };
|
||||
QPair<int, int> updated_messages = {0, 0};
|
||||
|
||||
if (messages.isEmpty()) {
|
||||
qDebugNN << "No messages to be updated/added in DB for feed"
|
||||
<< QUOTE_W_SPACE_DOT(feed->customId());
|
||||
qDebugNN << "No messages to be updated/added in DB for feed" << QUOTE_W_SPACE_DOT(feed->customId());
|
||||
return updated_messages;
|
||||
}
|
||||
|
||||
QList<RootItem*> items_to_update;
|
||||
bool is_main_thread = QThread::currentThread() == qApp->thread();
|
||||
|
||||
qDebugNN << LOGSEC_CORE
|
||||
<< "Updating messages in DB. Main thread:"
|
||||
<< QUOTE_W_SPACE_DOT(is_main_thread);
|
||||
qDebugNN << LOGSEC_CORE << "Updating messages in DB. Main thread:" << QUOTE_W_SPACE_DOT(is_main_thread);
|
||||
|
||||
bool ok = false;
|
||||
QSqlDatabase database = is_main_thread ?
|
||||
qApp->database()->driver()->connection(metaObject()->className()) :
|
||||
qApp->database()->driver()->connection(QSL("feed_upd"));
|
||||
QSqlDatabase database = is_main_thread ? qApp->database()->driver()->connection(metaObject()->className())
|
||||
: qApp->database()->driver()->connection(QSL("feed_upd"));
|
||||
|
||||
updated_messages = DatabaseQueries::updateMessages(database, messages, feed, force_update, &ok);
|
||||
|
||||
|
@ -22,11 +22,11 @@
|
||||
|
||||
GreaderNetwork::GreaderNetwork(QObject* parent)
|
||||
: QObject(parent), m_root(nullptr), m_service(GreaderServiceRoot::Service::FreshRss), m_username(QString()),
|
||||
m_password(QString()), m_baseUrl(QString()), m_batchSize(GREADER_DEFAULT_BATCH_SIZE), m_downloadOnlyUnreadMessages(false),
|
||||
m_prefetchedMessages({}), m_prefetchedStatus(Feed::Status::Normal), m_performGlobalFetching(false),
|
||||
m_intelligentSynchronization(true), m_newerThanFilter(QDate::currentDate().addYears(-1)),
|
||||
m_oauth(new OAuth2Service(QSL(INO_OAUTH_AUTH_URL), QSL(INO_OAUTH_TOKEN_URL),
|
||||
{}, {}, QSL(INO_OAUTH_SCOPE), this)) {
|
||||
m_password(QString()), m_baseUrl(QString()), m_batchSize(GREADER_DEFAULT_BATCH_SIZE),
|
||||
m_downloadOnlyUnreadMessages(false), m_prefetchedMessages({}), m_prefetchedStatus(Feed::Status::Normal),
|
||||
m_performGlobalFetching(false), m_intelligentSynchronization(true),
|
||||
m_newerThanFilter(QDate::currentDate().addYears(-1)),
|
||||
m_oauth(new OAuth2Service(QSL(INO_OAUTH_AUTH_URL), QSL(INO_OAUTH_TOKEN_URL), {}, {}, QSL(INO_OAUTH_SCOPE), this)) {
|
||||
initializeOauth();
|
||||
clearCredentials();
|
||||
}
|
||||
@ -44,13 +44,15 @@ QNetworkReply::NetworkError GreaderNetwork::editLabels(const QString& state,
|
||||
return network_err;
|
||||
}
|
||||
|
||||
QStringList trimmed_ids; trimmed_ids.reserve(msg_custom_ids.size());
|
||||
QStringList trimmed_ids;
|
||||
trimmed_ids.reserve(msg_custom_ids.size());
|
||||
|
||||
for (const QString& id : msg_custom_ids) {
|
||||
trimmed_ids.append(QSL("i=") + id);
|
||||
}
|
||||
|
||||
QStringList working_subset; working_subset.reserve(std::min(GREADER_API_EDIT_TAG_BATCH, int(trimmed_ids.size())));
|
||||
QStringList working_subset;
|
||||
working_subset.reserve(std::min(GREADER_API_EDIT_TAG_BATCH, int(trimmed_ids.size())));
|
||||
|
||||
// Now, we perform messages update in batches (max X messages per batch).
|
||||
while (!trimmed_ids.isEmpty()) {
|
||||
@ -76,18 +78,19 @@ QNetworkReply::NetworkError GreaderNetwork::editLabels(const QString& state,
|
||||
|
||||
// We send this batch.
|
||||
QByteArray output;
|
||||
auto result_edit = NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
args.toUtf8(),
|
||||
output,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{ authHeader(),
|
||||
{ QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(),
|
||||
QSL("application/x-www-form-urlencoded").toLocal8Bit() } },
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
auto result_edit =
|
||||
NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
args.toUtf8(),
|
||||
output,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{authHeader(),
|
||||
{QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(),
|
||||
QSL("application/x-www-form-urlencoded").toLocal8Bit()}},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result_edit.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
return result_edit.m_networkError;
|
||||
@ -115,7 +118,7 @@ QVariantHash GreaderNetwork::userInfo(const QNetworkProxy& proxy) {
|
||||
{},
|
||||
output,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{ authHeader() },
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
@ -135,7 +138,8 @@ void GreaderNetwork::clearPrefetchedMessages() {
|
||||
|
||||
void GreaderNetwork::prepareFeedFetching(GreaderServiceRoot* root,
|
||||
const QList<Feed*>& feeds,
|
||||
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>& stated_messages,
|
||||
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>&
|
||||
stated_messages,
|
||||
const QHash<QString, QStringList>& tagged_messages,
|
||||
const QNetworkProxy& proxy) {
|
||||
Q_UNUSED(tagged_messages)
|
||||
@ -148,9 +152,7 @@ void GreaderNetwork::prepareFeedFetching(GreaderServiceRoot* root,
|
||||
|
||||
m_performGlobalFetching = perc_of_fetching > GREADER_GLOBAL_UPDATE_THRES;
|
||||
|
||||
qDebugNN << LOGSEC_GREADER
|
||||
<< "Percentage of feeds for fetching:"
|
||||
<< QUOTE_W_SPACE_DOT(perc_of_fetching * 100.0);
|
||||
qDebugNN << LOGSEC_GREADER << "Percentage of feeds for fetching:" << QUOTE_W_SPACE_DOT(perc_of_fetching * 100.0);
|
||||
|
||||
auto remote_starred_ids_list = itemIds(QSL(GREADER_API_FULL_STATE_IMPORTANT), false, proxy, -1, m_newerThanFilter);
|
||||
|
||||
@ -174,10 +176,12 @@ void GreaderNetwork::prepareFeedFetching(GreaderServiceRoot* root,
|
||||
if (m_performGlobalFetching) {
|
||||
qWarningNN << LOGSEC_GREADER << "Performing global contents fetching.";
|
||||
|
||||
QStringList remote_all_ids_list = m_downloadOnlyUnreadMessages
|
||||
? QStringList()
|
||||
: itemIds(QSL(GREADER_API_FULL_STATE_READING_LIST), false, proxy, -1, m_newerThanFilter);
|
||||
QStringList remote_unread_ids_list = itemIds(QSL(GREADER_API_FULL_STATE_READING_LIST), true, proxy, -1, m_newerThanFilter);
|
||||
QStringList remote_all_ids_list =
|
||||
m_downloadOnlyUnreadMessages
|
||||
? QStringList()
|
||||
: itemIds(QSL(GREADER_API_FULL_STATE_READING_LIST), false, proxy, -1, m_newerThanFilter);
|
||||
QStringList remote_unread_ids_list =
|
||||
itemIds(QSL(GREADER_API_FULL_STATE_READING_LIST), true, proxy, -1, m_newerThanFilter);
|
||||
|
||||
for (int i = 0; i < remote_all_ids_list.size(); i++) {
|
||||
remote_all_ids_list.replace(i, convertShortStreamIdToLongStreamId(remote_all_ids_list.at(i)));
|
||||
@ -238,29 +242,24 @@ void GreaderNetwork::prepareFeedFetching(GreaderServiceRoot* root,
|
||||
catch (const FeedFetchException& fex) {
|
||||
m_prefetchedStatus = fex.feedStatus();
|
||||
|
||||
qCriticalNN << LOGSEC_CORE
|
||||
<< "Failed to fetch item IDs for common stream:"
|
||||
<< QUOTE_W_SPACE_DOT(fex.message());
|
||||
qCriticalNN << LOGSEC_CORE << "Failed to fetch item IDs for common stream:" << QUOTE_W_SPACE_DOT(fex.message());
|
||||
}
|
||||
catch (const NetworkException& nex) {
|
||||
m_prefetchedStatus = Feed::Status::NetworkError;
|
||||
|
||||
qCriticalNN << LOGSEC_CORE
|
||||
<< "Failed to fetch item IDs for common stream:"
|
||||
<< QUOTE_W_SPACE_DOT(nex.message());
|
||||
qCriticalNN << LOGSEC_CORE << "Failed to fetch item IDs for common stream:" << QUOTE_W_SPACE_DOT(nex.message());
|
||||
}
|
||||
catch (const ApplicationException& aex) {
|
||||
m_prefetchedStatus = Feed::Status::OtherError;
|
||||
|
||||
qCriticalNN << LOGSEC_CORE
|
||||
<< "Failed to fetch item IDs for common stream:"
|
||||
<< QUOTE_W_SPACE_DOT(aex.message());
|
||||
qCriticalNN << LOGSEC_CORE << "Failed to fetch item IDs for common stream:" << QUOTE_W_SPACE_DOT(aex.message());
|
||||
}
|
||||
}
|
||||
|
||||
QList<Message> GreaderNetwork::getMessagesIntelligently(ServiceRoot* root,
|
||||
const QString& stream_id,
|
||||
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
|
||||
const QHash<ServiceRoot::BagOfMessages, QStringList>&
|
||||
stated_messages,
|
||||
const QHash<QString, QStringList>& tagged_messages,
|
||||
Feed::Status& error,
|
||||
const QNetworkProxy& proxy) {
|
||||
@ -281,35 +280,28 @@ QList<Message> GreaderNetwork::getMessagesIntelligently(ServiceRoot* root,
|
||||
QStringList remote_all_ids_list, remote_unread_ids_list;
|
||||
|
||||
try {
|
||||
remote_all_ids_list = m_downloadOnlyUnreadMessages
|
||||
? QStringList()
|
||||
: itemIds(stream_id, false, proxy, -1, m_newerThanFilter);
|
||||
remote_all_ids_list =
|
||||
m_downloadOnlyUnreadMessages ? QStringList() : itemIds(stream_id, false, proxy, -1, m_newerThanFilter);
|
||||
remote_unread_ids_list = itemIds(stream_id, true, proxy, -1, m_newerThanFilter);
|
||||
}
|
||||
catch (const FeedFetchException& fex) {
|
||||
error = fex.feedStatus();
|
||||
|
||||
qCriticalNN << LOGSEC_CORE
|
||||
<< "Failed to fetch item IDs for specific stream:"
|
||||
<< QUOTE_W_SPACE_DOT(fex.message());
|
||||
qCriticalNN << LOGSEC_CORE << "Failed to fetch item IDs for specific stream:" << QUOTE_W_SPACE_DOT(fex.message());
|
||||
|
||||
return msgs;
|
||||
}
|
||||
catch (const NetworkException& nex) {
|
||||
error = Feed::Status::NetworkError;
|
||||
|
||||
qCriticalNN << LOGSEC_CORE
|
||||
<< "Failed to fetch item IDs for specific stream:"
|
||||
<< QUOTE_W_SPACE_DOT(nex.message());
|
||||
qCriticalNN << LOGSEC_CORE << "Failed to fetch item IDs for specific stream:" << QUOTE_W_SPACE_DOT(nex.message());
|
||||
|
||||
return msgs;
|
||||
}
|
||||
catch (const ApplicationException& aex) {
|
||||
error = Feed::Status::OtherError;
|
||||
|
||||
qCriticalNN << LOGSEC_CORE
|
||||
<< "Failed to fetch item IDs for specific stream:"
|
||||
<< QUOTE_W_SPACE_DOT(aex.message());
|
||||
qCriticalNN << LOGSEC_CORE << "Failed to fetch item IDs for specific stream:" << QUOTE_W_SPACE_DOT(aex.message());
|
||||
|
||||
return msgs;
|
||||
}
|
||||
@ -372,10 +364,9 @@ QList<Message> GreaderNetwork::getMessagesIntelligently(ServiceRoot* root,
|
||||
for (int i = 0; i < m_prefetchedMessages.size(); i++) {
|
||||
auto prefetched_msg = m_prefetchedMessages.at(i);
|
||||
|
||||
if (prefetched_msg.m_feedId == stream_id &&
|
||||
!boolinq::from(msgs).any([&prefetched_msg](const Message& ms) {
|
||||
return ms.m_customId == prefetched_msg.m_customId;
|
||||
})) {
|
||||
if (prefetched_msg.m_feedId == stream_id && !boolinq::from(msgs).any([&prefetched_msg](const Message& ms) {
|
||||
return ms.m_customId == prefetched_msg.m_customId;
|
||||
})) {
|
||||
msgs.append(prefetched_msg);
|
||||
m_prefetchedMessages.removeAt(i--);
|
||||
}
|
||||
@ -399,8 +390,11 @@ QNetworkReply::NetworkError GreaderNetwork::markMessagesStarred(RootItem::Import
|
||||
proxy);
|
||||
}
|
||||
|
||||
QStringList GreaderNetwork::itemIds(const QString& stream_id, bool unread_only, const QNetworkProxy& proxy,
|
||||
int max_count, QDate newer_than) {
|
||||
QStringList GreaderNetwork::itemIds(const QString& stream_id,
|
||||
bool unread_only,
|
||||
const QNetworkProxy& proxy,
|
||||
int max_count,
|
||||
QDate newer_than) {
|
||||
if (!ensureLogin(proxy)) {
|
||||
throw FeedFetchException(Feed::Status::AuthError, tr("login failed"));
|
||||
}
|
||||
@ -409,12 +403,10 @@ QStringList GreaderNetwork::itemIds(const QString& stream_id, bool unread_only,
|
||||
QStringList ids;
|
||||
|
||||
do {
|
||||
QString full_url = generateFullUrl(Operations::ItemIds).arg(m_service == GreaderServiceRoot::Service::TheOldReader
|
||||
? stream_id
|
||||
: QUrl::toPercentEncoding(stream_id),
|
||||
QString::number(max_count <= 0
|
||||
? GREADET_API_ITEM_IDS_MAX
|
||||
: max_count));
|
||||
QString full_url =
|
||||
generateFullUrl(Operations::ItemIds)
|
||||
.arg(m_service == GreaderServiceRoot::Service::TheOldReader ? stream_id : QUrl::toPercentEncoding(stream_id),
|
||||
QString::number(max_count <= 0 ? GREADET_API_ITEM_IDS_MAX : max_count));
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
if (unread_only) {
|
||||
@ -430,9 +422,10 @@ QStringList GreaderNetwork::itemIds(const QString& stream_id, bool unread_only,
|
||||
#if QT_VERSION < 0x050E00 // Qt < 5.14.0
|
||||
QDateTime(newer_than)
|
||||
#else
|
||||
newer_than.startOfDay()
|
||||
newer_than
|
||||
.startOfDay()
|
||||
#endif
|
||||
.toSecsSinceEpoch());
|
||||
.toSecsSinceEpoch());
|
||||
}
|
||||
|
||||
QByteArray output_stream;
|
||||
@ -441,18 +434,15 @@ QStringList GreaderNetwork::itemIds(const QString& stream_id, bool unread_only,
|
||||
{},
|
||||
output_stream,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{ authHeader() },
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result_stream.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER
|
||||
<< "Cannot download item IDs for "
|
||||
<< QUOTE_NO_SPACE(stream_id)
|
||||
<< ", network error:"
|
||||
<< QUOTE_W_SPACE_DOT(result_stream.m_networkError);
|
||||
qCriticalNN << LOGSEC_GREADER << "Cannot download item IDs for " << QUOTE_NO_SPACE(stream_id)
|
||||
<< ", network error:" << QUOTE_W_SPACE_DOT(result_stream.m_networkError);
|
||||
throw NetworkException(result_stream.m_networkError);
|
||||
}
|
||||
else {
|
||||
@ -464,8 +454,10 @@ QStringList GreaderNetwork::itemIds(const QString& stream_id, bool unread_only,
|
||||
return ids;
|
||||
}
|
||||
|
||||
QList<Message> GreaderNetwork::itemContents(ServiceRoot* root, const QList<QString>& stream_ids,
|
||||
Feed::Status& error, const QNetworkProxy& proxy) {
|
||||
QList<Message> GreaderNetwork::itemContents(ServiceRoot* root,
|
||||
const QList<QString>& stream_ids,
|
||||
Feed::Status& error,
|
||||
const QNetworkProxy& proxy) {
|
||||
QString continuation;
|
||||
|
||||
if (!ensureLogin(proxy)) {
|
||||
@ -477,12 +469,11 @@ QList<Message> GreaderNetwork::itemContents(ServiceRoot* root, const QList<QStri
|
||||
QList<QString> my_stream_ids(stream_ids);
|
||||
|
||||
while (!my_stream_ids.isEmpty()) {
|
||||
int batch = (m_service == GreaderServiceRoot::Service::TheOldReader ||
|
||||
m_service == GreaderServiceRoot::Service::FreshRss)
|
||||
? TOR_ITEM_CONTENTS_BATCH
|
||||
: (m_service == GreaderServiceRoot::Service::Inoreader
|
||||
? INO_ITEM_CONTENTS_BATCH
|
||||
: GREADER_API_ITEM_CONTENTS_BATCH);
|
||||
int batch =
|
||||
(m_service == GreaderServiceRoot::Service::TheOldReader || m_service == GreaderServiceRoot::Service::FreshRss)
|
||||
? TOR_ITEM_CONTENTS_BATCH
|
||||
: (m_service == GreaderServiceRoot::Service::Inoreader ? INO_ITEM_CONTENTS_BATCH
|
||||
: GREADER_API_ITEM_CONTENTS_BATCH);
|
||||
QList<QString> batch_ids = my_stream_ids.mid(0, batch);
|
||||
|
||||
my_stream_ids = my_stream_ids.mid(batch);
|
||||
@ -495,32 +486,32 @@ QList<Message> GreaderNetwork::itemContents(ServiceRoot* root, const QList<QStri
|
||||
full_url += QSL("&c=%1").arg(continuation);
|
||||
}
|
||||
|
||||
std::list inp = boolinq::from(batch_ids).select([this](const QString& id) {
|
||||
return QSL("i=%1").arg(m_service == GreaderServiceRoot::Service::TheOldReader
|
||||
? id
|
||||
: QUrl::toPercentEncoding(id));
|
||||
}).toStdList();
|
||||
std::list inp = boolinq::from(batch_ids)
|
||||
.select([this](const QString& id) {
|
||||
return QSL("i=%1").arg(m_service == GreaderServiceRoot::Service::TheOldReader
|
||||
? id
|
||||
: QUrl::toPercentEncoding(id));
|
||||
})
|
||||
.toStdList();
|
||||
QByteArray input = FROM_STD_LIST(QStringList, inp).join(QSL("&")).toUtf8();
|
||||
QByteArray output_stream;
|
||||
auto result_stream = NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
input,
|
||||
output_stream,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{ authHeader(),
|
||||
{ QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(),
|
||||
QSL("application/x-www-form-urlencoded").toLocal8Bit() } },
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
auto result_stream =
|
||||
NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
input,
|
||||
output_stream,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{authHeader(),
|
||||
{QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(),
|
||||
QSL("application/x-www-form-urlencoded").toLocal8Bit()}},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result_stream.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER
|
||||
<< "Cannot download messages for "
|
||||
<< batch_ids
|
||||
<< ", network error:"
|
||||
<< QUOTE_W_SPACE_DOT(result_stream.m_networkError);
|
||||
qCriticalNN << LOGSEC_GREADER << "Cannot download messages for " << batch_ids
|
||||
<< ", network error:" << QUOTE_W_SPACE_DOT(result_stream.m_networkError);
|
||||
error = Feed::Status::NetworkError;
|
||||
return {};
|
||||
}
|
||||
@ -535,8 +526,10 @@ QList<Message> GreaderNetwork::itemContents(ServiceRoot* root, const QList<QStri
|
||||
return msgs;
|
||||
}
|
||||
|
||||
QList<Message> GreaderNetwork::streamContents(ServiceRoot* root, const QString& stream_id,
|
||||
Feed::Status& error, const QNetworkProxy& proxy) {
|
||||
QList<Message> GreaderNetwork::streamContents(ServiceRoot* root,
|
||||
const QString& stream_id,
|
||||
Feed::Status& error,
|
||||
const QNetworkProxy& proxy) {
|
||||
QString continuation;
|
||||
|
||||
if (!ensureLogin(proxy)) {
|
||||
@ -545,15 +538,15 @@ QList<Message> GreaderNetwork::streamContents(ServiceRoot* root, const QString&
|
||||
}
|
||||
|
||||
QList<Message> msgs;
|
||||
int target_msgs_size = batchSize() <= 0 ? 2000000: batchSize();
|
||||
int target_msgs_size = batchSize() <= 0 ? 2000000 : batchSize();
|
||||
|
||||
do {
|
||||
QString full_url = generateFullUrl(Operations::StreamContents).arg(
|
||||
(m_service == GreaderServiceRoot::Service::TheOldReader ||
|
||||
m_service == GreaderServiceRoot::Service::FreshRss)
|
||||
? stream_id
|
||||
: QUrl::toPercentEncoding(stream_id),
|
||||
QString::number(target_msgs_size));
|
||||
QString full_url = generateFullUrl(Operations::StreamContents)
|
||||
.arg((m_service == GreaderServiceRoot::Service::TheOldReader ||
|
||||
m_service == GreaderServiceRoot::Service::FreshRss)
|
||||
? stream_id
|
||||
: QUrl::toPercentEncoding(stream_id),
|
||||
QString::number(target_msgs_size));
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
if (downloadOnlyUnreadMessages()) {
|
||||
@ -569,9 +562,10 @@ QList<Message> GreaderNetwork::streamContents(ServiceRoot* root, const QString&
|
||||
#if QT_VERSION < 0x050E00 // Qt < 5.14.0
|
||||
QDateTime(m_newerThanFilter)
|
||||
#else
|
||||
m_newerThanFilter.startOfDay()
|
||||
m_newerThanFilter
|
||||
.startOfDay()
|
||||
#endif
|
||||
.toSecsSinceEpoch());
|
||||
.toSecsSinceEpoch());
|
||||
}
|
||||
|
||||
QByteArray output_stream;
|
||||
@ -580,18 +574,15 @@ QList<Message> GreaderNetwork::streamContents(ServiceRoot* root, const QString&
|
||||
{},
|
||||
output_stream,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{ authHeader() },
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result_stream.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER
|
||||
<< "Cannot download messages for "
|
||||
<< QUOTE_NO_SPACE(stream_id)
|
||||
<< ", network error:"
|
||||
<< QUOTE_W_SPACE_DOT(result_stream.m_networkError);
|
||||
qCriticalNN << LOGSEC_GREADER << "Cannot download messages for " << QUOTE_NO_SPACE(stream_id)
|
||||
<< ", network error:" << QUOTE_W_SPACE_DOT(result_stream.m_networkError);
|
||||
error = Feed::Status::NetworkError;
|
||||
return {};
|
||||
}
|
||||
@ -610,6 +601,7 @@ RootItem* GreaderNetwork::categoriesFeedsLabelsTree(bool obtain_icons, const QNe
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
if (!ensureLogin(proxy)) {
|
||||
qCriticalNN << LOGSEC_GREADER << "Cannot get feed tree, not logged-in.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -619,7 +611,7 @@ RootItem* GreaderNetwork::categoriesFeedsLabelsTree(bool obtain_icons, const QNe
|
||||
{},
|
||||
output_labels,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{ authHeader() },
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
@ -636,28 +628,31 @@ RootItem* GreaderNetwork::categoriesFeedsLabelsTree(bool obtain_icons, const QNe
|
||||
{},
|
||||
output_feeds,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{ authHeader() },
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (result_feeds.m_networkError != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER
|
||||
<< "Cannot get feed tree, network error:" << QUOTE_W_SPACE_DOT(result_feeds.m_networkError);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return decodeTagsSubscriptions(output_labels, output_feeds, obtain_icons, proxy);
|
||||
}
|
||||
|
||||
RootItem* GreaderNetwork::decodeTagsSubscriptions(const QString& categories, const QString& feeds,
|
||||
bool obtain_icons, const QNetworkProxy& proxy) {
|
||||
RootItem* GreaderNetwork::decodeTagsSubscriptions(const QString& categories,
|
||||
const QString& feeds,
|
||||
bool obtain_icons,
|
||||
const QNetworkProxy& proxy) {
|
||||
auto* parent = new RootItem();
|
||||
QMap<QString, RootItem*> cats;
|
||||
QList<RootItem*> lbls;
|
||||
QJsonArray json;
|
||||
|
||||
if (m_service == GreaderServiceRoot::Service::Bazqux ||
|
||||
m_service == GreaderServiceRoot::Service::Reedah ||
|
||||
if (m_service == GreaderServiceRoot::Service::Bazqux || m_service == GreaderServiceRoot::Service::Reedah ||
|
||||
m_service == GreaderServiceRoot::Service::Inoreader) {
|
||||
// We need to process subscription list first and extract categories.
|
||||
json = QJsonDocument::fromJson(feeds.toUtf8()).object()[QSL("subscriptions")].toArray();
|
||||
@ -691,8 +686,7 @@ RootItem* GreaderNetwork::decodeTagsSubscriptions(const QString& categories, con
|
||||
QString label_id = label[QSL("id")].toString();
|
||||
|
||||
if ((label[QSL("type")].toString() == QSL("folder") ||
|
||||
(m_service == GreaderServiceRoot::Service::TheOldReader &&
|
||||
label_id.contains(QSL("/label/"))))) {
|
||||
(m_service == GreaderServiceRoot::Service::TheOldReader && label_id.contains(QSL("/label/"))))) {
|
||||
|
||||
// We have category (not "state" or "tag" or "label").
|
||||
auto* category = new Category();
|
||||
@ -711,8 +705,7 @@ RootItem* GreaderNetwork::decodeTagsSubscriptions(const QString& categories, con
|
||||
new_lbl->setCustomId(label_id);
|
||||
lbls.append(new_lbl);
|
||||
}
|
||||
else if ((m_service == GreaderServiceRoot::Service::Bazqux ||
|
||||
m_service == GreaderServiceRoot::Service::Reedah ||
|
||||
else if ((m_service == GreaderServiceRoot::Service::Bazqux || m_service == GreaderServiceRoot::Service::Reedah ||
|
||||
m_service == GreaderServiceRoot::Service::Inoreader) &&
|
||||
label_id.contains(QSL("/label/"))) {
|
||||
if (!cats.contains(label_id)) {
|
||||
@ -775,18 +768,14 @@ RootItem* GreaderNetwork::decodeTagsSubscriptions(const QString& categories, con
|
||||
}
|
||||
}
|
||||
|
||||
icon_urls.append({ icon_url, true });
|
||||
icon_urls.append({icon_url, true});
|
||||
}
|
||||
|
||||
icon_urls.append({ url, false });
|
||||
icon_urls.append({url, false});
|
||||
|
||||
QIcon icon;
|
||||
|
||||
if (NetworkFactory::downloadIcon(icon_urls,
|
||||
1000,
|
||||
icon,
|
||||
{},
|
||||
proxy) == QNetworkReply::NetworkError::NoError) {
|
||||
if (NetworkFactory::downloadIcon(icon_urls, 1000, icon, {}, proxy) == QNetworkReply::NetworkError::NoError) {
|
||||
feed->setIcon(icon);
|
||||
}
|
||||
}
|
||||
@ -808,21 +797,22 @@ QNetworkReply::NetworkError GreaderNetwork::clientLogin(const QNetworkProxy& pro
|
||||
QString full_url = generateFullUrl(Operations::ClientLogin);
|
||||
auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
QByteArray output;
|
||||
QByteArray args = QSL("Email=%1&Passwd=%2").arg(QString::fromLocal8Bit(QUrl::toPercentEncoding(username())),
|
||||
QString::fromLocal8Bit(QUrl::toPercentEncoding(password()))).toLocal8Bit();
|
||||
auto network_result = NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
args,
|
||||
output,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{ {
|
||||
QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(),
|
||||
QSL("application/x-www-form-urlencoded").toLocal8Bit()
|
||||
} },
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
QByteArray args = QSL("Email=%1&Passwd=%2")
|
||||
.arg(QString::fromLocal8Bit(QUrl::toPercentEncoding(username())),
|
||||
QString::fromLocal8Bit(QUrl::toPercentEncoding(password())))
|
||||
.toLocal8Bit();
|
||||
auto network_result =
|
||||
NetworkFactory::performNetworkOperation(full_url,
|
||||
timeout,
|
||||
args,
|
||||
output,
|
||||
QNetworkAccessManager::Operation::PostOperation,
|
||||
{{QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(),
|
||||
QSL("application/x-www-form-urlencoded").toLocal8Bit()}},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
proxy);
|
||||
|
||||
if (network_result.m_networkError == QNetworkReply::NetworkError::NoError) {
|
||||
// Save credentials.
|
||||
@ -867,7 +857,7 @@ QNetworkReply::NetworkError GreaderNetwork::clientLogin(const QNetworkProxy& pro
|
||||
args,
|
||||
output,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
{ authHeader() },
|
||||
{authHeader()},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
@ -919,12 +909,10 @@ void GreaderNetwork::setBaseUrl(const QString& base_url) {
|
||||
|
||||
QPair<QByteArray, QByteArray> GreaderNetwork::authHeader() const {
|
||||
if (m_service == GreaderServiceRoot::Service::Inoreader) {
|
||||
return { QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
|
||||
m_oauth->bearer().toLocal8Bit() };
|
||||
return {QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), m_oauth->bearer().toLocal8Bit()};
|
||||
}
|
||||
else {
|
||||
return { QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
|
||||
QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit() };
|
||||
return {QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit()};
|
||||
}
|
||||
}
|
||||
|
||||
@ -942,8 +930,7 @@ bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy, QNetworkReply::Netw
|
||||
|
||||
if (login != QNetworkReply::NetworkError::NoError) {
|
||||
qCriticalNN << LOGSEC_GREADER
|
||||
<< "Login failed with error:"
|
||||
<< QUOTE_W_SPACE_DOT(NetworkFactory::networkErrorText(login));
|
||||
<< "Login failed with error:" << QUOTE_W_SPACE_DOT(NetworkFactory::networkErrorText(login));
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@ -955,8 +942,9 @@ bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy, QNetworkReply::Netw
|
||||
}
|
||||
|
||||
QString GreaderNetwork::convertLongStreamIdToShortStreamId(const QString& stream_id) const {
|
||||
return QString::number(QString(stream_id).replace(QSL("tag:google.com,2005:reader/item/"),
|
||||
QString()).toULongLong(nullptr, 16));
|
||||
return QString::number(QString(stream_id)
|
||||
.replace(QSL("tag:google.com,2005:reader/item/"), QString())
|
||||
.toULongLong(nullptr, 16));
|
||||
}
|
||||
|
||||
QString GreaderNetwork::convertShortStreamIdToLongStreamId(const QString& stream_id) const {
|
||||
@ -968,10 +956,7 @@ QString GreaderNetwork::convertShortStreamIdToLongStreamId(const QString& stream
|
||||
return QSL("tag:google.com,2005:reader/item/%1").arg(stream_id);
|
||||
}
|
||||
else {
|
||||
return QSL("tag:google.com,2005:reader/item/%1").arg(stream_id.toULongLong(),
|
||||
16,
|
||||
16,
|
||||
QL1C('0'));
|
||||
return QSL("tag:google.com,2005:reader/item/%1").arg(stream_id.toULongLong(), 16, 16, QL1C('0'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1064,9 +1049,8 @@ QList<Message> GreaderNetwork::decodeStreamContents(ServiceRoot* root,
|
||||
|
||||
message.m_contents = message_obj[QSL("summary")].toObject()[QSL("content")].toString();
|
||||
message.m_rawContents = QJsonDocument(message_obj).toJson(QJsonDocument::JsonFormat::Compact);
|
||||
message.m_feedId = stream_id.isEmpty()
|
||||
? message_obj[QSL("origin")].toObject()[QSL("streamId")].toString()
|
||||
: stream_id;
|
||||
message.m_feedId =
|
||||
stream_id.isEmpty() ? message_obj[QSL("origin")].toObject()[QSL("streamId")].toString() : stream_id;
|
||||
|
||||
if (message.m_title.isEmpty()) {
|
||||
message.m_title = message.m_url;
|
||||
@ -1091,9 +1075,7 @@ void GreaderNetwork::clearCredentials() {
|
||||
}
|
||||
|
||||
QString GreaderNetwork::sanitizedBaseUrl() const {
|
||||
QString base_url = m_service == GreaderServiceRoot::Service::Inoreader
|
||||
? QSL(GREADER_URL_INOREADER)
|
||||
: m_baseUrl;
|
||||
QString base_url = m_service == GreaderServiceRoot::Service::Inoreader ? QSL(GREADER_URL_INOREADER) : m_baseUrl;
|
||||
|
||||
if (!base_url.endsWith('/')) {
|
||||
base_url = base_url + QL1C('/');
|
||||
@ -1148,29 +1130,27 @@ QString GreaderNetwork::generateFullUrl(GreaderNetwork::Operations operation) co
|
||||
void GreaderNetwork::onTokensError(const QString& error, const QString& error_description) {
|
||||
Q_UNUSED(error)
|
||||
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure, {
|
||||
tr("Inoreader: authentication error"),
|
||||
tr("Click this to login again. Error is: '%1'").arg(error_description),
|
||||
QSystemTrayIcon::MessageIcon::Critical },
|
||||
{}, {
|
||||
tr("Login"),
|
||||
[this]() {
|
||||
m_oauth->setAccessToken(QString());
|
||||
m_oauth->setRefreshToken(QString());
|
||||
m_oauth->login();
|
||||
} });
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure,
|
||||
{tr("Inoreader: authentication error"),
|
||||
tr("Click this to login again. Error is: '%1'").arg(error_description),
|
||||
QSystemTrayIcon::MessageIcon::Critical},
|
||||
{},
|
||||
{tr("Login"), [this]() {
|
||||
m_oauth->setAccessToken(QString());
|
||||
m_oauth->setRefreshToken(QString());
|
||||
m_oauth->login();
|
||||
}});
|
||||
}
|
||||
|
||||
void GreaderNetwork::onAuthFailed() {
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure, {
|
||||
tr("Inoreader: authorization denied"),
|
||||
tr("Click this to login again."),
|
||||
QSystemTrayIcon::MessageIcon::Critical },
|
||||
{}, {
|
||||
tr("Login"),
|
||||
[this]() {
|
||||
m_oauth->login();
|
||||
} });
|
||||
qApp->showGuiMessage(Notification::Event::LoginFailure,
|
||||
{tr("Inoreader: authorization denied"),
|
||||
tr("Click this to login again."),
|
||||
QSystemTrayIcon::MessageIcon::Critical},
|
||||
{},
|
||||
{tr("Login"), [this]() {
|
||||
m_oauth->login();
|
||||
}});
|
||||
}
|
||||
|
||||
void GreaderNetwork::initializeOauth() {
|
||||
@ -1179,23 +1159,23 @@ void GreaderNetwork::initializeOauth() {
|
||||
m_oauth->setClientSecretSecret(TextFactory::decrypt(QSL(INOREADER_CLIENT_SECRET), OAUTH_DECRYPTION_KEY));
|
||||
#endif
|
||||
|
||||
m_oauth->setRedirectUrl(QSL(OAUTH_REDIRECT_URI) +
|
||||
QL1C(':') +
|
||||
QString::number(INO_OAUTH_REDIRECT_URI_PORT),
|
||||
false);
|
||||
m_oauth->setRedirectUrl(QSL(OAUTH_REDIRECT_URI) + QL1C(':') + QString::number(INO_OAUTH_REDIRECT_URI_PORT), false);
|
||||
|
||||
connect(m_oauth, &OAuth2Service::tokensRetrieveError, this, &GreaderNetwork::onTokensError);
|
||||
connect(m_oauth, &OAuth2Service::authFailed, this, &GreaderNetwork::onAuthFailed);
|
||||
connect(m_oauth, &OAuth2Service::tokensRetrieved, this, [this](QString access_token, QString refresh_token, int expires_in) {
|
||||
Q_UNUSED(expires_in)
|
||||
Q_UNUSED(access_token)
|
||||
connect(m_oauth,
|
||||
&OAuth2Service::tokensRetrieved,
|
||||
this,
|
||||
[this](QString access_token, QString refresh_token, int expires_in) {
|
||||
Q_UNUSED(expires_in)
|
||||
Q_UNUSED(access_token)
|
||||
|
||||
if (m_root != nullptr && m_root->accountId() > 0 && !refresh_token.isEmpty()) {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||
if (m_root != nullptr && m_root->accountId() > 0 && !refresh_token.isEmpty()) {
|
||||
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
|
||||
|
||||
DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_root->accountId());
|
||||
}
|
||||
});
|
||||
DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_root->accountId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QDate GreaderNetwork::newerThanFilter() const {
|
||||
|
Loading…
x
Reference in New Issue
Block a user