From 5ebad9060dc55c847c56b99a13dc40ff86be0acf Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Wed, 24 Jun 2020 10:01:47 +0200 Subject: [PATCH] Make oauth2 process work universally, fix some fucked-up port information, remove internal oauth2 login gui and rely on system web browser. --- localization/rssguard_sv.ts | 32 +-- src/librssguard/definitions/definitions.h | 3 +- src/librssguard/gui/dialogs/oauthlogin.cpp | 48 ---- src/librssguard/gui/dialogs/oauthlogin.h | 34 --- src/librssguard/gui/dialogs/oauthlogin.ui | 59 ----- src/librssguard/librssguard.pro | 21 +- src/librssguard/network-web/oauth2service.cpp | 64 ++---- src/librssguard/network-web/oauth2service.h | 18 +- .../network-web/oauthhttphandler.cpp | 6 +- .../gmail/gui/formeditgmailaccount.cpp | 12 +- .../gmail/gui/formeditgmailaccount.ui | 213 +++++++++--------- .../gui/formeditinoreaderaccount.cpp | 11 +- .../inoreader/gui/formeditinoreaderaccount.ui | 213 +++++++++--------- 13 files changed, 287 insertions(+), 447 deletions(-) delete mode 100644 src/librssguard/gui/dialogs/oauthlogin.cpp delete mode 100644 src/librssguard/gui/dialogs/oauthlogin.h delete mode 100644 src/librssguard/gui/dialogs/oauthlogin.ui diff --git a/localization/rssguard_sv.ts b/localization/rssguard_sv.ts index 1560674da..749725c0f 100644 --- a/localization/rssguard_sv.ts +++ b/localization/rssguard_sv.ts @@ -230,11 +230,11 @@ Removing starred messages... - + Tar bort stjärnmärkta meddelanden... Starred messages purged... - + Stjärnmärkta meddelanden rensat... @@ -275,7 +275,7 @@ Unknown error: '%1'. Unknown MySQL error arised. - + Okänt fel: "%1" @@ -1239,47 +1239,47 @@ att funktionen inte är implementerad än. Download only unread messages - + Ladda bara ner olästa meddelanden Password for your Nextcloud account - + Lösenord för ditt NextCloud-konto Username for your Nextcloud account - + Användarnamn för ditt NextCloud-konto URL of your Nextcloud server, without any API path - + URL för din NextCloud-server, utan API-sökväg Add new Nextcloud News account - + Lägg till nytt NextCloud News-konto Edit existing Nextcloud News account - + Redigera befintligt NextCloud News-konto Selected Nextcloud News server is running unsupported version (%1). At least version %2 is required. - + Den valda NextCloud News-servern kör en version (%1) som inte stöds. Lägst version %2 är ett krav. Selected Nextcloud News server is running unsupported version. - + Den valda NextCloud News-servern kör en version som inte stöds. Nextcloud News server is okay, running with version %1, while at least version %2 is required. - + NextClouds nyhetsserver fungerar med version %1, då minst version %2 krävs. Nextcloud News server is okay. - + NextClouds nyhetsserver är okay. Network error, have you entered correct Nextcloud endpoint and password? - + Nätverksfel! Har du angett korrekt NextCloud-slutpunkt och lösenord? @@ -1450,7 +1450,7 @@ att funktionen inte är implementerad än. Download only unread messages - + Ladda bara ner olästa meddelanden. @@ -2122,7 +2122,7 @@ att funktionen inte är implementerad än. Show only &unread messages - + Visa endast &olästa meddelanden diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index fd4a60a6c..237b8e63f 100755 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -13,7 +13,6 @@ #define ARGUMENTS_LIST_SEPARATOR "\n" -#define LOCALHOST_ADDRESS "http://localhost" #define ADBLOCK_ADBLOCKED_PAGE "adblockedpage" #define ADBLOCK_HOWTO_FILTERS "http://adblockplus.org/en/filters" #define ADBLOCK_UPDATE_DAYS_INTERVAL 5 @@ -51,6 +50,8 @@ #define ELLIPSIS_LENGTH 3 #define MIN_CATEGORY_NAME_LENGTH 1 #define DEFAULT_AUTO_UPDATE_INTERVAL 15 +#define OAUTH_REDIRECT_URI_PORT 13377 +#define OAUTH_REDIRECT_URI "http://localhost:13377" #define AUTO_UPDATE_INTERVAL 60000 #define STARTUP_UPDATE_DELAY 15.0 // In seconds. #define TIMEZONE_OFFSET_LIMIT 6 diff --git a/src/librssguard/gui/dialogs/oauthlogin.cpp b/src/librssguard/gui/dialogs/oauthlogin.cpp deleted file mode 100644 index 51d256811..000000000 --- a/src/librssguard/gui/dialogs/oauthlogin.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// For license of this file, see /LICENSE.md. - -#include "gui/dialogs/oauthlogin.h" - -#include "gui/guiutilities.h" -#include "miscellaneous/application.h" -#include "miscellaneous/iconfactory.h" - -#include -#include -#include - -OAuthLogin::OAuthLogin(QWidget* parent) : QDialog(parent) { - m_ui.setupUi(this); - - GuiUtilities::applyDialogProperties(*this); - - connect(this, &OAuthLogin::rejected, this, &OAuthLogin::authRejected); - connect(m_ui.m_loginPage, &WebViewer::urlChanged, this, &OAuthLogin::urlChanged); -} - -void OAuthLogin::login(const QString& consentPageUrl, const QString& redirect_uri) { - m_ui.m_loginPage->page()->profile()->clearHttpCache(); - m_ui.m_loginPage->page()->profile()->cookieStore()->deleteAllCookies(); - - m_redirectUri = redirect_uri; - m_ui.m_loginPage->setUrl(QUrl(consentPageUrl)); - m_ui.m_buttonBox->setFocus(); - exec(); -} - -void OAuthLogin::urlChanged(QUrl url) { - QString redirected_uri = url.toString(); - QUrlQuery query(QUrl(redirected_uri).query()); - - if (redirected_uri.startsWith(m_redirectUri)) { - if (query.hasQueryItem(QSL("code"))) { - emit authGranted(query.queryItemValue(QSL("code"))); - - accept(); - } - else { - emit authRejected(); - - reject(); - } - } -} diff --git a/src/librssguard/gui/dialogs/oauthlogin.h b/src/librssguard/gui/dialogs/oauthlogin.h deleted file mode 100644 index fab071493..000000000 --- a/src/librssguard/gui/dialogs/oauthlogin.h +++ /dev/null @@ -1,34 +0,0 @@ -// For license of this file, see /LICENSE.md. - -#ifndef OAUTHLOGIN_H -#define OAUTHLOGIN_H - -#include - -#include "ui_oauthlogin.h" - -namespace Ui { - class OAuthLogin; -} - -class OAuthLogin : public QDialog { - Q_OBJECT - - public: - explicit OAuthLogin(QWidget* parent = 0); - - void login(const QString& consentPageUrl, const QString& redirect_uri); - - private slots: - void urlChanged(QUrl url); - - signals: - void authRejected(); - void authGranted(QString authCode); - - private: - Ui::OAuthLogin m_ui; - QString m_redirectUri; -}; - -#endif // OAUTHLOGIN_H diff --git a/src/librssguard/gui/dialogs/oauthlogin.ui b/src/librssguard/gui/dialogs/oauthlogin.ui deleted file mode 100644 index 066725e0b..000000000 --- a/src/librssguard/gui/dialogs/oauthlogin.ui +++ /dev/null @@ -1,59 +0,0 @@ - - - OAuthLogin - - - - 0 - 0 - 577 - 412 - - - - Access authorization to service is requested - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel - - - - - - - - WebViewer - QWidget -
webviewer.h
- 1 -
-
- - - - m_buttonBox - rejected() - OAuthLogin - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff --git a/src/librssguard/librssguard.pro b/src/librssguard/librssguard.pro index 73dba5619..88494d129 100644 --- a/src/librssguard/librssguard.pro +++ b/src/librssguard/librssguard.pro @@ -171,7 +171,8 @@ HEADERS += core/feeddownloader.h \ network-web/httpresponse.h \ services/gmail/gui/formdownloadattachment.h \ services/gmail/gui/formaddeditemail.h \ - gui/searchtextwidget.h + gui/searchtextwidget.h \ + network-web/oauthhttphandler.h SOURCES += core/feeddownloader.cpp \ core/feedsmodel.cpp \ @@ -309,7 +310,8 @@ SOURCES += core/feeddownloader.cpp \ network-web/httpresponse.cpp \ services/gmail/gui/formdownloadattachment.cpp \ services/gmail/gui/formaddeditemail.cpp \ - gui/searchtextwidget.cpp + gui/searchtextwidget.cpp \ + network-web/oauthhttphandler.cpp mac { OBJECTIVE_SOURCES += miscellaneous/disablewindowtabbing.mm @@ -352,8 +354,7 @@ equals(USE_WEBENGINE, true) { gui/discoverfeedsbutton.h \ network-web/googlesuggest.h \ network-web/webpage.h \ - network-web/rssguardschemehandler.h \ - gui/dialogs/oauthlogin.h + network-web/rssguardschemehandler.h SOURCES += gui/locationlineedit.cpp \ gui/webviewer.cpp \ @@ -361,8 +362,7 @@ equals(USE_WEBENGINE, true) { gui/discoverfeedsbutton.cpp \ network-web/googlesuggest.cpp \ network-web/webpage.cpp \ - network-web/rssguardschemehandler.cpp \ - gui/dialogs/oauthlogin.cpp + network-web/rssguardschemehandler.cpp # Add AdBlock sources. HEADERS += network-web/adblock/adblockaddsubscriptiondialog.h \ @@ -393,19 +393,16 @@ equals(USE_WEBENGINE, true) { gui/treewidget.cpp FORMS += network-web/adblock/adblockaddsubscriptiondialog.ui \ - network-web/adblock/adblockdialog.ui \ - gui/dialogs/oauthlogin.ui + network-web/adblock/adblockdialog.ui } else { HEADERS += gui/messagepreviewer.h \ gui/messagetextbrowser.h \ - gui/newspaperpreviewer.h \ - network-web/oauthhttphandler.h + gui/newspaperpreviewer.h SOURCES += gui/messagepreviewer.cpp \ gui/messagetextbrowser.cpp \ - gui/newspaperpreviewer.cpp \ - network-web/oauthhttphandler.cpp + gui/newspaperpreviewer.cpp FORMS += gui/messagepreviewer.ui \ gui/newspaperpreviewer.ui diff --git a/src/librssguard/network-web/oauth2service.cpp b/src/librssguard/network-web/oauth2service.cpp index eecbc079d..f6ddb507b 100644 --- a/src/librssguard/network-web/oauth2service.cpp +++ b/src/librssguard/network-web/oauth2service.cpp @@ -27,30 +27,26 @@ #include "definitions/definitions.h" #include "miscellaneous/application.h" #include "network-web/networkfactory.h" +#include "network-web/oauthhttphandler.h" #include "network-web/webfactory.h" #include "services/inoreader/definitions.h" -#if defined(USE_WEBENGINE) -#include "gui/dialogs/oauthlogin.h" -#else -#include "network-web/oauthhttphandler.h" - -Q_GLOBAL_STATIC(OAuthHttpHandler, qz_silent_acmanager) -#endif - -#include - #include +#include #include #include #include #include + +#include #include +Q_GLOBAL_STATIC(OAuthHttpHandler, qz_silent_acmanager) + 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(std::rand())), m_timerId(-1) { - m_redirectUrl = QSL(LOCALHOST_ADDRESS); + m_redirectUrl = QSL(OAUTH_REDIRECT_URI); m_tokenGrantType = QSL("authorization_code"); m_tokenUrl = QUrl(token_url); m_authUrl = auth_url; @@ -60,8 +56,6 @@ OAuth2Service::OAuth2Service(const QString& auth_url, const QString& token_url, m_scope = scope; connect(&m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(tokenRequestFinished(QNetworkReply*))); - -#if !defined(USE_WEBENGINE) connect(handler(), &OAuthHttpHandler::authGranted, [this](const QString& auth_code, const QString& id) { if (id.isEmpty() || id == m_id) { // We process this further only if handler (static singleton) responded to our original request. @@ -76,7 +70,6 @@ OAuth2Service::OAuth2Service(const QString& auth_url, const QString& token_url, emit authFailed(); } }); -#endif } QString OAuth2Service::bearer() { @@ -137,13 +130,10 @@ void OAuth2Service::setId(const QString& id) { m_id = id; } -#if !defined(USE_WEBENGINE) OAuthHttpHandler* OAuth2Service::handler() { return qz_silent_acmanager(); } -#endif - void OAuth2Service::retrieveAccessToken(const QString& auth_code) { QNetworkRequest networkRequest; @@ -270,7 +260,7 @@ void OAuth2Service::setRefreshToken(const QString& refresh_token) { } bool OAuth2Service::login() { - bool did_token_expire = tokensExpireIn().isNull() || tokensExpireIn() < QDateTime::currentDateTime(); + bool did_token_expire = tokensExpireIn().isNull() || tokensExpireIn() < QDateTime::currentDateTime().addSecs(-120); bool does_token_exist = !refreshToken().isEmpty(); // We refresh current tokens only if: @@ -309,30 +299,20 @@ void OAuth2Service::killRefreshTimer() { } void OAuth2Service::retrieveAuthCode() { - QString auth_url = m_authUrl + QString("?client_id=%1&scope=%2&" - "redirect_uri=%3&response_type=code&state=%4&" - "prompt=consent&access_type=offline").arg(m_clientId, - m_scope, - m_redirectUrl, - m_id); - -#if defined(USE_WEBENGINE) - OAuthLogin login_page(qApp->mainFormWidget()); - - connect(&login_page, &OAuthLogin::authGranted, this, &OAuth2Service::retrieveAccessToken); - connect(&login_page, &OAuthLogin::authRejected, this, [this]() { - logout(); - emit authFailed(); - }); - - qApp->showGuiMessage(tr("Logging in via OAuth 2.0..."), - tr("Requesting access authorization for '%1'...").arg(m_authUrl), - QSystemTrayIcon::MessageIcon::Information); - - login_page.login(auth_url, m_redirectUrl); -#else + QString auth_url = m_authUrl + QString("?client_id=%1&" + "scope=%2&" + "redirect_uri=%3&" + "response_type=code&" + "state=%4&" + "prompt=consent&" + "access_type=offline").arg(m_clientId, m_scope, m_redirectUrl, m_id); // We run login URL in external browser, response is caught by light HTTP server. - qApp->web()->openUrlInExternalBrowser(auth_url); -#endif + if (qApp->web()->openUrlInExternalBrowser(auth_url)) { + QInputDialog::getText(qApp->mainFormWidget(), + tr("Navigate to website"), + tr("To login, you need to navigate to this website:"), + QLineEdit::EchoMode::Normal, + auth_url); + } } diff --git a/src/librssguard/network-web/oauth2service.h b/src/librssguard/network-web/oauth2service.h index 9bd46f9dd..5d8199892 100644 --- a/src/librssguard/network-web/oauth2service.h +++ b/src/librssguard/network-web/oauth2service.h @@ -28,11 +28,8 @@ #include #include -#include "network-web/silentnetworkaccessmanager.h" - -#if !defined(USE_WEBENGINE) #include "network-web/oauthhttphandler.h" -#endif +#include "network-web/silentnetworkaccessmanager.h" class OAuth2Service : public QObject { Q_OBJECT @@ -43,10 +40,9 @@ class OAuth2Service : public QObject { const QString& scope, QObject* parent = nullptr); // Returns bearer HTTP header value. - // NOTE: Only call this if isFullyLoggedIn() - // returns true. If isFullyLoggedIn() returns - // false, then you must call login() on - // main GUI thread. + // NOTE: If on working thread, then call this only if isFullyLoggedIn() + // returns true. If isFullyLoggedIn() returns false, then you must call login() on + // main GUI thread first. QString bearer(); bool isFullyLoggedIn() const; @@ -89,6 +85,7 @@ class OAuth2Service : public QObject { // Performs login if needed. If some refresh token is set, then // the initial "auth" step is skipped and attempt to refresh // access token is made. + // // Returns true, if user is already logged in (final state). // Returns false, if user is NOT logged in (asynchronous flow). // @@ -119,12 +116,7 @@ class OAuth2Service : public QObject { QString m_authUrl; QString m_scope; SilentNetworkAccessManager m_networkManager; - -#if !defined(USE_WEBENGINE) - - // Returns pointer to global silent network manager static OAuthHttpHandler* handler(); -#endif }; #endif // OAUTH2SERVICE_H diff --git a/src/librssguard/network-web/oauthhttphandler.cpp b/src/librssguard/network-web/oauthhttphandler.cpp index 66685787a..ef02946f2 100644 --- a/src/librssguard/network-web/oauthhttphandler.cpp +++ b/src/librssguard/network-web/oauthhttphandler.cpp @@ -15,8 +15,8 @@ OAuthHttpHandler::OAuthHttpHandler(QObject* parent) : QObject(parent) { connect(&m_httpServer, &QTcpServer::newConnection, this, &OAuthHttpHandler::clientConnected); - if (!m_httpServer.listen(m_listenAddress, 13377)) { - qCritical("OAuth HTTP handler: Failed to start listening."); + if (!m_httpServer.listen(m_listenAddress, OAUTH_REDIRECT_URI_PORT)) { + qCritical("OAuth HTTP handler: Failed to start listening on port '%d'.", OAUTH_REDIRECT_URI_PORT); } } @@ -73,7 +73,7 @@ void OAuthHttpHandler::answerClient(QTcpSocket* socket, const QUrl& url) { const QUrlQuery query(url.query()); const auto items = query.queryItems(); - for (const auto & item : items) { + for (const auto& item : items) { received_data.insert(item.first, item.second); } diff --git a/src/librssguard/services/gmail/gui/formeditgmailaccount.cpp b/src/librssguard/services/gmail/gui/formeditgmailaccount.cpp index f3f4963bb..46b85c593 100644 --- a/src/librssguard/services/gmail/gui/formeditgmailaccount.cpp +++ b/src/librssguard/services/gmail/gui/formeditgmailaccount.cpp @@ -15,15 +15,11 @@ FormEditGmailAccount::FormEditGmailAccount(QWidget* parent) : QDialog(parent), m_ui.setupUi(this); GuiUtilities::setLabelAsNotice(*m_ui.m_lblAuthInfo, true); - -#if !defined(USE_WEBENGINE) - m_ui.m_lblAuthInfo->setText(tr("You must use \"%1\" as base redirect URL. You can use custom port to make sure " - "that no local service occupies it. Make sure that this redirect URL matches redirect " - "URL of used \"application\".").arg(LOCALHOST_ADDRESS)); -#endif - GuiUtilities::applyDialogProperties(*this, qApp->icons()->miscIcon(QSL("gmail"))); + m_ui.m_lblAuthInfo->setText(tr("You must use \"%1\" as redirect URL. It is important to leave this " + "URL intact, because %2 is waiting on specified port for " + "service tokens.").arg(OAUTH_REDIRECT_URI, APP_NAME)); m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Information, tr("Not tested yet."), tr("Not tested yet.")); @@ -150,7 +146,7 @@ GmailServiceRoot* FormEditGmailAccount::execForCreate() { m_ui.m_txtAppId->lineEdit()->clear(); m_ui.m_txtAppKey->lineEdit()->clear(); - m_ui.m_txtRedirectUrl->lineEdit()->setText(LOCALHOST_ADDRESS); + m_ui.m_txtRedirectUrl->lineEdit()->setText(OAUTH_REDIRECT_URI); exec(); diff --git a/src/librssguard/services/gmail/gui/formeditgmailaccount.ui b/src/librssguard/services/gmail/gui/formeditgmailaccount.ui index 24662262e..d50cead4c 100644 --- a/src/librssguard/services/gmail/gui/formeditgmailaccount.ui +++ b/src/librssguard/services/gmail/gui/formeditgmailaccount.ui @@ -10,119 +10,115 @@ 363 - - - + + + + + Username + + + + + + + + + + OAuth 2.0 settings + + + + + + Application ID + + + + + + + + + + Application key + + + + + + + + + + Redirect URL + + + + + + + + + + Predefined settings DO NOT have to be changed from their default values. Change these values only of you are advanced user and you know what you are doing! + + + true + + + + + + + + - + - Username + Only download newest X messages per feed - - - - - - OAuth 2.0 settings + + + + 140 + 16777215 + + + + message(s) - - - - - Application ID - - - - - - - - - - Application key - - - - - - - - - - Redirect URL - - - - - - - - - - Predefined settings DO NOT have to be changed from their default values. Change these values only of you are advanced user and you know what you are doing! - - - true - - - - - - - - - - Only download newest X messages per feed - - - - - - - - 140 - 16777215 - - - - message(s) - - - - - - - - - - - &Login - - - - - - - - 0 - 1 - - - - Qt::RightToLeft - - - - - - + + + + + + &Login + + + + + + + + 0 + 1 + + + + Qt::RightToLeft + + + + + + Qt::Horizontal @@ -132,6 +128,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.cpp b/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.cpp index e3955b5df..475aab2eb 100644 --- a/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.cpp +++ b/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.cpp @@ -17,12 +17,9 @@ FormEditInoreaderAccount::FormEditInoreaderAccount(QWidget* parent) : QDialog(pa GuiUtilities::setLabelAsNotice(*m_ui.m_lblAuthInfo, true); GuiUtilities::applyDialogProperties(*this, qApp->icons()->miscIcon(QSL("inoreader"))); -#if !defined(USE_WEBENGINE) - m_ui.m_lblAuthInfo->setText(tr("You must use \"%1\" as base redirect URL. You can use custom port to make sure " - "that no local service occupies it. Make sure that this redirect URL matches redirect " - "URL of used \"application\".").arg(LOCALHOST_ADDRESS)); -#endif - + m_ui.m_lblAuthInfo->setText(tr("You must use \"%1\" as redirect URL. It is important to leave this " + "URL intact, because %2 is waiting on specified port for " + "service tokens.").arg(OAUTH_REDIRECT_URI, APP_NAME)); m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Information, tr("Not tested yet."), tr("Not tested yet.")); @@ -147,7 +144,7 @@ InoreaderServiceRoot* FormEditInoreaderAccount::execForCreate() { m_ui.m_txtAppId->lineEdit()->setText(INOREADER_OAUTH_CLI_ID); m_ui.m_txtAppKey->lineEdit()->setText(INOREADER_OAUTH_CLI_KEY); - m_ui.m_txtRedirectUrl->lineEdit()->setText(LOCALHOST_ADDRESS); + m_ui.m_txtRedirectUrl->lineEdit()->setText(OAUTH_REDIRECT_URI); exec(); diff --git a/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.ui b/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.ui index a659a151b..084894c79 100644 --- a/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.ui +++ b/src/librssguard/services/inoreader/gui/formeditinoreaderaccount.ui @@ -10,119 +10,115 @@ 363 - - - + + + + + Username + + + + + + + + + + OAuth 2.0 settings + + + + + + Application ID + + + + + + + + + + Application key + + + + + + + + + + Redirect URL + + + + + + + + + + These settings DO NOT have to be changed from their default values. Change these values only of you are advanced user and you know what you are doing! + + + true + + + + + + + + - + - Username + Only download newest X messages per feed - - - - - - OAuth 2.0 settings + + + + 140 + 16777215 + + + + message(s) - - - - - Application ID - - - - - - - - - - Application key - - - - - - - - - - Redirect URL - - - - - - - - - - These settings DO NOT have to be changed from their default values. Change these values only of you are advanced user and you know what you are doing! - - - true - - - - - - - - - - Only download newest X messages per feed - - - - - - - - 140 - 16777215 - - - - message(s) - - - - - - - - - - - &Login - - - - - - - - 0 - 1 - - - - Qt::RightToLeft - - - - - - + + + + + + &Login + + + + + + + + 0 + 1 + + + + Qt::RightToLeft + + + + + + Qt::Horizontal @@ -132,6 +128,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + +